diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs index 94ae2ac71..85313dc20 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs @@ -3,84150 +3,3910 @@ // 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; /// -/// Provides generated Porter-Duff blenders for associated-alpha pixel formats. +/// Provides the associated-alpha equations consumed by the shared pixel-blending traversal. /// -internal static partial class AssociatedAlphaPixelBlenders - where TPixel : unmanaged, IPixel +internal static class AssociatedAlphaPixelBlenderOperators { - /// - /// A pixel blender that implements the "NormalSrc" composition equation. + /// Applies the "NormalSrc" associated-alpha composition equation. /// - public sealed class NormalSrc : AssociatedAlphaPixelBlender + public readonly struct NormalSrc : IPixelBlenderOperator { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrc Instance { get; } = new NormalSrc(); - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.NormalSrc(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrc(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrc(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrc(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "MultiplySrc" associated-alpha composition equation. + /// + public readonly struct MultiplySrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrc(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrc(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrc(background, source, amount); + } - 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.NormalSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrc(background[i], source[i], amount); - } - } - } + /// + /// Applies the "AddSrc" associated-alpha composition equation. + /// + public readonly struct AddSrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddSrc(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrc(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrc(background, source, amount); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); + /// + /// Applies the "SubtractSrc" associated-alpha composition equation. + /// + public readonly struct SubtractSrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrc(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrc(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrc(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); + /// + /// Applies the "ScreenSrc" associated-alpha composition equation. + /// + public readonly struct ScreenSrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrc(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrc(background, source, amount); - 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.NormalSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrc(background[i], source, amount); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrc(background, source, amount); + } + /// + /// Applies the "DarkenSrc" associated-alpha composition equation. + /// + public readonly struct DarkenSrc : IPixelBlenderOperator + { /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrc(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrc(background, source, amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrc(background, source, amount); + } - 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); + /// + /// Applies the "LightenSrc" associated-alpha composition equation. + /// + public readonly struct LightenSrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrc(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrc(background, source, amount); - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrc(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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrc(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "OverlaySrc" associated-alpha composition equation. + /// + public readonly struct OverlaySrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrc(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrc(background, source, amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrc(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Applies the "HardLightSrc" associated-alpha composition equation. + /// + public readonly struct HardLightSrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrc(background, source, amount); - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrc(background, source, amount); - 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.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrc(background, source, amount); + } + /// + /// Applies the "NormalSrcAtop" associated-alpha composition equation. + /// + public readonly struct NormalSrcAtop : IPixelBlenderOperator + { /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(background, source, amount); - Vector512 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 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(background, source, amount); + } - 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); + /// + /// Applies the "MultiplySrcAtop" associated-alpha composition equation. + /// + public readonly struct MultiplySrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(background, source, amount); - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 4); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "AddSrcAtop" associated-alpha composition equation. + /// + public readonly struct AddSrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcAtop(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcAtop(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcAtop(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Applies the "SubtractSrcAtop" associated-alpha composition equation. + /// + public readonly struct SubtractSrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(background, source, amount); - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(background, source, amount); - 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.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(background, source, amount); + } + /// + /// Applies the "ScreenSrcAtop" associated-alpha composition equation. + /// + public readonly struct ScreenSrcAtop : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(background, source, amount); + } - 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); + /// + /// Applies the "DarkenSrcAtop" associated-alpha composition equation. + /// + public readonly struct DarkenSrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(background, source, amount); - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(background, source, amount); - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrc(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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.NormalSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "LightenSrcAtop" associated-alpha composition equation. + /// + public readonly struct LightenSrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(background, source, amount); + } - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrc(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); - } + /// + /// Applies the "OverlaySrcAtop" associated-alpha composition equation. + /// + public readonly struct OverlaySrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(background, source, amount); - 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.NormalSrc(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.NormalSrc(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(background, source, amount); /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(background, source, amount); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Applies the "HardLightSrcAtop" associated-alpha composition equation. + /// + public readonly struct HardLightSrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(background, source, amount); - Vector512 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 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(background, source, amount); + } - 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); + /// + /// Applies the "NormalSrcOver" associated-alpha composition equation. + /// + public readonly struct NormalSrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcOver(background, source, amount); - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcOver(background, source, amount); - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrc(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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcOver(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.NormalSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "MultiplySrcOver" associated-alpha composition equation. + /// + public readonly struct MultiplySrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "AddSrcOver" associated-alpha composition equation. + /// + public readonly struct AddSrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcOver(background, source, amount); - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcOver(background, source, amount); - 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.NormalSrc(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.NormalSrc(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcOver(background, source, amount); + } + /// + /// Applies the "SubtractSrcOver" associated-alpha composition equation. + /// + public readonly struct SubtractSrcOver : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(background, source, amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(background, source, amount); + } - 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); + /// + /// Applies the "ScreenSrcOver" associated-alpha composition equation. + /// + public readonly struct ScreenSrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(background, source, amount); - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(background, source, amount); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Applies the "DarkenSrcOver" associated-alpha composition equation. + /// + public readonly struct DarkenSrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(background, source, amount); - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.NormalSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Applies the "LightenSrcOver" associated-alpha composition equation. + /// + public readonly struct LightenSrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcOver(background, source, amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcOver(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcOver(background, source, amount); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "OverlaySrcOver" associated-alpha composition equation. + /// + public readonly struct OverlaySrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(background, source, amount); - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(background, source, amount); - 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.NormalSrc(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.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(background, source, amount); + } + /// + /// Applies the "HardLightSrcOver" associated-alpha composition equation. + /// + public readonly struct HardLightSrcOver : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(background, source, amount); - Vector512 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 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(background, source, amount); + } - 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); + /// + /// Applies the "NormalSrcIn" associated-alpha composition equation. + /// + public readonly struct NormalSrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcIn(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcIn(background, source, amount); - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcIn(background, source, amount); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Applies the "MultiplySrcIn" associated-alpha composition equation. + /// + public readonly struct MultiplySrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(background, source, amount); - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.NormalSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Applies the "AddSrcIn" associated-alpha composition equation. + /// + public readonly struct AddSrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcIn(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcIn(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcIn(background, source, amount); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "SubtractSrcIn" associated-alpha composition equation. + /// + public readonly struct SubtractSrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(background, source, amount); - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(background, source, amount); - 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.NormalSrc(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.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(background, source, amount); } /// - /// A pixel blender that implements the "MultiplySrc" composition equation. + /// Applies the "ScreenSrcIn" associated-alpha composition equation. /// - public sealed class MultiplySrc : AssociatedAlphaPixelBlender + public readonly struct ScreenSrcIn : IPixelBlenderOperator { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrc Instance { get; } = new MultiplySrc(); + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(background, source, amount); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.MultiplySrc(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(background, source, amount); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Applies the "DarkenSrcIn" associated-alpha composition equation. + /// + public readonly struct DarkenSrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "LightenSrcIn" associated-alpha composition equation. + /// + public readonly struct LightenSrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcIn(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcIn(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcIn(background, source, amount); + } - 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.MultiplySrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrc(background[i], source[i], amount); - } - } - } + /// + /// Applies the "OverlaySrcIn" associated-alpha composition equation. + /// + public readonly struct OverlaySrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(background, source, amount); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); + /// + /// Applies the "HardLightSrcIn" associated-alpha composition equation. + /// + public readonly struct HardLightSrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); + /// + /// Applies the "NormalSrcOut" associated-alpha composition equation. + /// + public readonly struct NormalSrcOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcOut(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcOut(background, source, amount); - 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.MultiplySrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrc(background[i], source, amount); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcOut(background, source, amount); + } + /// + /// Applies the "MultiplySrcOut" associated-alpha composition equation. + /// + public readonly struct MultiplySrcOut : IPixelBlenderOperator + { /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(background, source, amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(background, source, amount); + } - 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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.MultiplySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "AddSrcOut" associated-alpha composition equation. + /// + public readonly struct AddSrcOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcOut(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcOut(background, source, amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcOut(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Applies the "SubtractSrcOut" associated-alpha composition equation. + /// + public readonly struct SubtractSrcOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(background, source, amount); - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(background, source, amount); - 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.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(background, source, amount); + } + /// + /// Applies the "ScreenSrcOut" associated-alpha composition equation. + /// + public readonly struct ScreenSrcOut : IPixelBlenderOperator + { /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(background, source, amount); - Vector512 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 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(background, source, amount); + } - 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); + /// + /// Applies the "DarkenSrcOut" associated-alpha composition equation. + /// + public readonly struct DarkenSrcOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(background, source, amount); - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 4); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "LightenSrcOut" associated-alpha composition equation. + /// + public readonly struct LightenSrcOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcOut(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcOut(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcOut(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Applies the "OverlaySrcOut" associated-alpha composition equation. + /// + public readonly struct OverlaySrcOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(background, source, amount); - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(background, source, amount); - 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.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(background, source, amount); + } + /// + /// Applies the "HardLightSrcOut" associated-alpha composition equation. + /// + public readonly struct HardLightSrcOut : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(background, source, amount); + } - 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); + /// + /// Applies the "NormalDest" associated-alpha composition equation. + /// + public readonly struct NormalDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalDest(background, source, amount); - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDest(background, source, amount); - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDest(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "MultiplyDest" associated-alpha composition equation. + /// + public readonly struct MultiplyDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDest(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDest(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDest(background, source, amount); + } - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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); - } + /// + /// Applies the "AddDest" associated-alpha composition equation. + /// + public readonly struct AddDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddDest(background, source, amount); - 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.MultiplySrc(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.MultiplySrc(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddDest(background, source, amount); /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddDest(background, source, amount); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Applies the "SubtractDest" associated-alpha composition equation. + /// + public readonly struct SubtractDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDest(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDest(background, source, amount); - Vector512 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 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDest(background, source, amount); + } - 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); + /// + /// Applies the "ScreenDest" associated-alpha composition equation. + /// + public readonly struct ScreenDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDest(background, source, amount); - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDest(background, source, amount); - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDest(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "DarkenDest" associated-alpha composition equation. + /// + public readonly struct DarkenDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDest(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDest(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDest(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "LightenDest" associated-alpha composition equation. + /// + public readonly struct LightenDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenDest(background, source, amount); - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDest(background, source, amount); - 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.MultiplySrc(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.MultiplySrc(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDest(background, source, amount); + } + /// + /// Applies the "OverlayDest" associated-alpha composition equation. + /// + public readonly struct OverlayDest : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDest(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDest(background, source, amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDest(background, source, amount); + } - 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); + /// + /// Applies the "HardLightDest" associated-alpha composition equation. + /// + public readonly struct HardLightDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDest(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDest(background, source, amount); - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDest(background, source, amount); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Applies the "NormalDestAtop" associated-alpha composition equation. + /// + public readonly struct NormalDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestAtop(background, source, amount); - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestAtop(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestAtop(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Applies the "MultiplyDestAtop" associated-alpha composition equation. + /// + public readonly struct MultiplyDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(background, source, amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(background, source, amount); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "AddDestAtop" associated-alpha composition equation. + /// + public readonly struct AddDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddDestAtop(background, source, amount); - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestAtop(background, source, amount); - 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.MultiplySrc(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.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestAtop(background, source, amount); + } + /// + /// Applies the "SubtractDestAtop" associated-alpha composition equation. + /// + public readonly struct SubtractDestAtop : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(background, source, amount); - Vector512 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 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(background, source, amount); + } - 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); + /// + /// Applies the "ScreenDestAtop" associated-alpha composition equation. + /// + public readonly struct ScreenDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(background, source, amount); - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(background, source, amount); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Applies the "DarkenDestAtop" associated-alpha composition equation. + /// + public readonly struct DarkenDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(background, source, amount); - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Applies the "LightenDestAtop" associated-alpha composition equation. + /// + public readonly struct LightenDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestAtop(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestAtop(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestAtop(background, source, amount); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "OverlayDestAtop" associated-alpha composition equation. + /// + public readonly struct OverlayDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(background, source, amount); - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(background, source, amount); - 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.MultiplySrc(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.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(background, source, amount); } /// - /// A pixel blender that implements the "AddSrc" composition equation. + /// Applies the "HardLightDestAtop" associated-alpha composition equation. /// - public sealed class AddSrc : AssociatedAlphaPixelBlender + public readonly struct HardLightDestAtop : IPixelBlenderOperator { - /// - /// Gets the static instance of this blender. - /// - public static AddSrc Instance { get; } = new AddSrc(); - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.AddSrc(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + /// Applies the "NormalDestOver" associated-alpha composition equation. + /// + public readonly struct NormalDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestOver(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestOver(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestOver(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + /// Applies the "MultiplyDestOver" associated-alpha composition equation. + /// + public readonly struct MultiplyDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(background, source, amount); - 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.AddSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrc(background[i], source[i], amount); - } - } - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(background, source, amount); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Applies the "AddDestOver" associated-alpha composition equation. + /// + public readonly struct AddDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddDestOver(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestOver(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestOver(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "SubtractDestOver" associated-alpha composition equation. + /// + public readonly struct SubtractDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestOver(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestOver(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestOver(background, source, amount); + } - 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.AddSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrc(background[i], source, amount); - } - } - } + /// + /// Applies the "ScreenDestOver" associated-alpha composition equation. + /// + public readonly struct ScreenDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestOver(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestOver(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestOver(background, source, amount); + } - Vector512 vOne = Vector512.Create(1F); + /// + /// Applies the "DarkenDestOver" associated-alpha composition equation. + /// + public readonly struct DarkenDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestOver(background, source, amount); - 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); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestOver(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestOver(background, source, amount); + } - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrc(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); - } + /// + /// Applies the "LightenDestOver" associated-alpha composition equation. + /// + public readonly struct LightenDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestOver(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestOver(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestOver(background, source, amount); + } - Vector256 vOne = Vector256.Create(1F); + /// + /// Applies the "OverlayDestOver" associated-alpha composition equation. + /// + public readonly struct OverlayDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestOver(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestOver(background, source, amount); - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrc(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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestOver(background, source, amount); + } - 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.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + /// Applies the "HardLightDestOver" associated-alpha composition equation. + /// + public readonly struct HardLightDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestOver(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestOver(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestOver(background, source, amount); + } - Vector512 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 vOne = Vector512.Create(1F); + /// + /// Applies the "NormalDestIn" associated-alpha composition equation. + /// + public readonly struct NormalDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestIn(background, source, amount); - 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); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestIn(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestIn(background, source, amount); + } - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 4); - } + /// + /// Applies the "MultiplyDestIn" associated-alpha composition equation. + /// + public readonly struct MultiplyDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(background, source, amount); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + /// Applies the "AddDestIn" associated-alpha composition equation. + /// + public readonly struct AddDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddDestIn(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestIn(background, source, amount); - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestIn(background, source, amount); + } - 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.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + /// Applies the "SubtractDestIn" associated-alpha composition equation. + /// + public readonly struct SubtractDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestIn(background, source, amount); /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrc(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.AddSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrc(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.AddSrc(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.AddSrc(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestIn(background, source, amount); /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestIn(background, source, amount); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Applies the "ScreenDestIn" associated-alpha composition equation. + /// + public readonly struct ScreenDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestIn(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestIn(background, source, amount); - Vector512 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 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestIn(background, source, amount); + } - 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); + /// + /// Applies the "DarkenDestIn" associated-alpha composition equation. + /// + public readonly struct DarkenDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestIn(background, source, amount); - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestIn(background, source, amount); - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrc(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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestIn(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.AddSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "LightenDestIn" associated-alpha composition equation. + /// + public readonly struct LightenDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestIn(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestIn(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestIn(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "OverlayDestIn" associated-alpha composition equation. + /// + public readonly struct OverlayDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestIn(background, source, amount); - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestIn(background, source, amount); - 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.AddSrc(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.AddSrc(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestIn(background, source, amount); + } + /// + /// Applies the "HardLightDestIn" associated-alpha composition equation. + /// + public readonly struct HardLightDestIn : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestIn(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestIn(background, source, amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestIn(background, source, amount); + } - 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); + /// + /// Applies the "NormalDestOut" associated-alpha composition equation. + /// + public readonly struct NormalDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestOut(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestOut(background, source, amount); - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestOut(background, source, amount); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Applies the "MultiplyDestOut" associated-alpha composition equation. + /// + public readonly struct MultiplyDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(background, source, amount); - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.AddSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Applies the "AddDestOut" associated-alpha composition equation. + /// + public readonly struct AddDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddDestOut(background, source, amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestOut(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestOut(background, source, amount); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "SubtractDestOut" associated-alpha composition equation. + /// + public readonly struct SubtractDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestOut(background, source, amount); - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestOut(background, source, amount); - 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.AddSrc(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.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestOut(background, source, amount); + } + /// + /// Applies the "ScreenDestOut" associated-alpha composition equation. + /// + public readonly struct ScreenDestOut : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestOut(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestOut(background, source, amount); - Vector512 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 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestOut(background, source, amount); + } - 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); + /// + /// Applies the "DarkenDestOut" associated-alpha composition equation. + /// + public readonly struct DarkenDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestOut(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestOut(background, source, amount); - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestOut(background, source, amount); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Applies the "LightenDestOut" associated-alpha composition equation. + /// + public readonly struct LightenDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestOut(background, source, amount); - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestOut(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.AddSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestOut(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Applies the "OverlayDestOut" associated-alpha composition equation. + /// + public readonly struct OverlayDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestOut(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestOut(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestOut(background, source, amount); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "HardLightDestOut" associated-alpha composition equation. + /// + public readonly struct HardLightDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestOut(background, source, amount); - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestOut(background, source, amount); - 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.AddSrc(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.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestOut(background, source, amount); } /// - /// A pixel blender that implements the "SubtractSrc" composition equation. + /// Applies the "NormalClear" associated-alpha composition equation. /// - public sealed class SubtractSrc : AssociatedAlphaPixelBlender + public readonly struct NormalClear : IPixelBlenderOperator { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrc Instance { get; } = new SubtractSrc(); - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.SubtractSrc(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalClear(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalClear(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalClear(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + /// Applies the "MultiplyClear" associated-alpha composition equation. + /// + public readonly struct MultiplyClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyClear(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyClear(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyClear(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + /// Applies the "AddClear" associated-alpha composition equation. + /// + public readonly struct AddClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddClear(background, source, amount); - 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.SubtractSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrc(background[i], source[i], amount); - } - } - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddClear(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddClear(background, source, amount); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Applies the "SubtractClear" associated-alpha composition equation. + /// + public readonly struct SubtractClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractClear(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractClear(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractClear(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "ScreenClear" associated-alpha composition equation. + /// + public readonly struct ScreenClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenClear(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenClear(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenClear(background, source, amount); + } - 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.SubtractSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrc(background[i], source, amount); - } - } - } + /// + /// Applies the "DarkenClear" associated-alpha composition equation. + /// + public readonly struct DarkenClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenClear(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenClear(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenClear(background, source, amount); + } - Vector512 vOne = Vector512.Create(1F); + /// + /// Applies the "LightenClear" associated-alpha composition equation. + /// + public readonly struct LightenClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenClear(background, source, amount); - 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); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenClear(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenClear(background, source, amount); + } - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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); - } + /// + /// Applies the "OverlayClear" associated-alpha composition equation. + /// + public readonly struct OverlayClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayClear(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayClear(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayClear(background, source, amount); + } - Vector256 vOne = Vector256.Create(1F); + /// + /// Applies the "HardLightClear" associated-alpha composition equation. + /// + public readonly struct HardLightClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightClear(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightClear(background, source, amount); - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightClear(background, source, amount); + } - 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.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + /// Applies the "NormalXor" associated-alpha composition equation. + /// + public readonly struct NormalXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalXor(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalXor(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalXor(background, source, amount); + } - Vector512 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 vOne = Vector512.Create(1F); + /// + /// Applies the "MultiplyXor" associated-alpha composition equation. + /// + public readonly struct MultiplyXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyXor(background, source, amount); - 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); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyXor(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyXor(background, source, amount); + } - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 4); - } + /// + /// Applies the "AddXor" associated-alpha composition equation. + /// + public readonly struct AddXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddXor(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddXor(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddXor(background, source, amount); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + /// Applies the "SubtractXor" associated-alpha composition equation. + /// + public readonly struct SubtractXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractXor(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractXor(background, source, amount); - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractXor(background, source, amount); + } - 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.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + /// Applies the "ScreenXor" associated-alpha composition equation. + /// + public readonly struct ScreenXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenXor(background, source, amount); /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenXor(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenXor(background, source, amount); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + /// Applies the "DarkenXor" associated-alpha composition equation. + /// + public readonly struct DarkenXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenXor(background, source, amount); - 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); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenXor(background, source, amount); - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenXor(background, source, amount); + } - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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); - } + /// + /// Applies the "LightenXor" associated-alpha composition equation. + /// + public readonly struct LightenXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenXor(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenXor(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenXor(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "OverlayXor" associated-alpha composition equation. + /// + public readonly struct OverlayXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayXor(background, source, amount); - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayXor(background, source, amount); - 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.SubtractSrc(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.SubtractSrc(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayXor(background, source, amount); + } + /// + /// Applies the "HardLightXor" associated-alpha composition equation. + /// + public readonly struct HardLightXor : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightXor(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightXor(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightXor(background, source, amount); + } - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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); +/// +/// Provides associated-alpha pixel blenders for each color and alpha composition pair. +/// +/// The associated-alpha destination pixel format. +internal static partial class AssociatedAlphaPixelBlenders + where TPixel : unmanaged, IPixel +{ + /// + /// Blends pixels with the "NormalSrc" composition equation. + /// + public sealed class NormalSrc : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalSrc Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "MultiplySrc" composition equation. + /// + public sealed class MultiplySrc : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplySrc Instance { get; } = new(); + } - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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); - } + /// + /// Blends pixels with the "AddSrc" composition equation. + /// + public sealed class AddSrc : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddSrc Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "SubtractSrc" composition equation. + /// + public sealed class SubtractSrc : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractSrc Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "ScreenSrc" composition equation. + /// + public sealed class ScreenSrc : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenSrc Instance { get; } = new(); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "DarkenSrc" composition equation. + /// + public sealed class DarkenSrc : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenSrc Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "LightenSrc" composition equation. + /// + public sealed class LightenSrc : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenSrc Instance { get; } = new(); + } - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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); - } + /// + /// Blends pixels with the "OverlaySrc" composition equation. + /// + public sealed class OverlaySrc : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlaySrc Instance { get; } = new(); + } - 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.SubtractSrc(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.SubtractSrc(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "HardLightSrc" composition equation. + /// + public sealed class HardLightSrc : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightSrc Instance { get; } = new(); + } - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "NormalSrcAtop" composition equation. + /// + public sealed class NormalSrcAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalSrcAtop Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "MultiplySrcAtop" composition equation. + /// + public sealed class MultiplySrcAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplySrcAtop Instance { get; } = new(); + } - Vector512 vOne = Vector512.Create(1F); + /// + /// Blends pixels with the "AddSrcAtop" composition equation. + /// + public sealed class AddSrcAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddSrcAtop Instance { get; } = new(); + } - 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); + /// + /// Blends pixels with the "SubtractSrcAtop" composition equation. + /// + public sealed class SubtractSrcAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractSrcAtop Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + /// Blends pixels with the "ScreenSrcAtop" composition equation. + /// + public sealed class ScreenSrcAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenSrcAtop Instance { get; } = new(); + } - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + /// Blends pixels with the "DarkenSrcAtop" composition equation. + /// + public sealed class DarkenSrcAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenSrcAtop Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "LightenSrcAtop" composition equation. + /// + public sealed class LightenSrcAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenSrcAtop Instance { get; } = new(); + } - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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); - } + /// + /// Blends pixels with the "OverlaySrcAtop" composition equation. + /// + public sealed class OverlaySrcAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlaySrcAtop Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "HardLightSrcAtop" composition equation. + /// + public sealed class HardLightSrcAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightSrcAtop Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "NormalSrcOver" composition equation. + /// + public sealed class NormalSrcOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalSrcOver Instance { get; } = new(); + } - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "MultiplySrcOver" composition equation. + /// + public sealed class MultiplySrcOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplySrcOver Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Blends pixels with the "AddSrcOver" composition equation. + /// + public sealed class AddSrcOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddSrcOver Instance { get; } = new(); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "SubtractSrcOver" composition equation. + /// + public sealed class SubtractSrcOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractSrcOver Instance { get; } = new(); + } - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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); - } + /// + /// Blends pixels with the "ScreenSrcOver" composition equation. + /// + public sealed class ScreenSrcOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenSrcOver Instance { get; } = new(); + } - 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.SubtractSrc(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.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "DarkenSrcOver" composition equation. + /// + public sealed class DarkenSrcOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenSrcOver Instance { get; } = new(); + } - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "LightenSrcOver" composition equation. + /// + public sealed class LightenSrcOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenSrcOver Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "OverlaySrcOver" composition equation. + /// + public sealed class OverlaySrcOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlaySrcOver Instance { get; } = new(); + } - Vector512 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 vOne = Vector512.Create(1F); + /// + /// Blends pixels with the "HardLightSrcOver" composition equation. + /// + public sealed class HardLightSrcOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightSrcOver Instance { get; } = new(); + } - 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); + /// + /// Blends pixels with the "NormalSrcIn" composition equation. + /// + public sealed class NormalSrcIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalSrcIn Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + /// Blends pixels with the "MultiplySrcIn" composition equation. + /// + public sealed class MultiplySrcIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplySrcIn Instance { get; } = new(); + } - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + /// Blends pixels with the "AddSrcIn" composition equation. + /// + public sealed class AddSrcIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddSrcIn Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "SubtractSrcIn" composition equation. + /// + public sealed class SubtractSrcIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractSrcIn Instance { get; } = new(); + } - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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); - } + /// + /// Blends pixels with the "ScreenSrcIn" composition equation. + /// + public sealed class ScreenSrcIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenSrcIn Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "DarkenSrcIn" composition equation. + /// + public sealed class DarkenSrcIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenSrcIn Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "LightenSrcIn" composition equation. + /// + public sealed class LightenSrcIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenSrcIn Instance { get; } = new(); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "OverlaySrcIn" composition equation. + /// + public sealed class OverlaySrcIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlaySrcIn Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Blends pixels with the "HardLightSrcIn" composition equation. + /// + public sealed class HardLightSrcIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightSrcIn Instance { get; } = new(); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "NormalSrcOut" composition equation. + /// + public sealed class NormalSrcOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalSrcOut Instance { get; } = new(); + } - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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); - } + /// + /// Blends pixels with the "MultiplySrcOut" composition equation. + /// + public sealed class MultiplySrcOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplySrcOut Instance { get; } = new(); + } - 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.SubtractSrc(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.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "AddSrcOut" composition equation. + /// + public sealed class AddSrcOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddSrcOut Instance { get; } = new(); } /// - /// A pixel blender that implements the "ScreenSrc" composition equation. + /// Blends pixels with the "SubtractSrcOut" composition equation. /// - public sealed class ScreenSrc : AssociatedAlphaPixelBlender + public sealed class SubtractSrcOut : + AssociatedAlphaPixelBlender { /// - /// Gets the static instance of this blender. + /// Gets the shared blender instance. /// - public static ScreenSrc Instance { get; } = new ScreenSrc(); + public static SubtractSrcOut Instance { get; } = new(); + } - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.ScreenSrc(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } + /// + /// Blends pixels with the "ScreenSrcOut" composition equation. + /// + public sealed class ScreenSrcOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenSrcOut Instance { get; } = new(); + } - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + /// + /// Blends pixels with the "DarkenSrcOut" composition equation. + /// + public sealed class DarkenSrcOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenSrcOut Instance { get; } = new(); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "LightenSrcOut" composition equation. + /// + public sealed class LightenSrcOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenSrcOut Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); + /// + /// Blends pixels with the "OverlaySrcOut" composition equation. + /// + public sealed class OverlaySrcOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlaySrcOut Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + /// Blends pixels with the "HardLightSrcOut" composition equation. + /// + public sealed class HardLightSrcOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightSrcOut Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "NormalDest" composition equation. + /// + public sealed class NormalDest : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalDest Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + /// + /// Blends pixels with the "MultiplyDest" composition equation. + /// + public sealed class MultiplyDest : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyDest Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrc(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(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.ScreenSrc(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(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.ScreenSrc(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(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.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrc(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.ScreenSrc(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.ScreenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrc" composition equation. - /// - public sealed class DarkenSrc : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrc Instance { get; } = new DarkenSrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.DarkenSrc(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrc(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(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.DarkenSrc(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(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.DarkenSrc(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(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.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrc(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.DarkenSrc(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.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrc" composition equation. - /// - public sealed class LightenSrc : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrc Instance { get; } = new LightenSrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.LightenSrc(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrc(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(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.LightenSrc(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(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.LightenSrc(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(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.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrc(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.LightenSrc(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.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrc" composition equation. - /// - public sealed class OverlaySrc : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrc Instance { get; } = new OverlaySrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.OverlaySrc(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrc(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(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.OverlaySrc(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(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.OverlaySrc(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(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.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrc(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.OverlaySrc(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.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrc" composition equation. - /// - public sealed class HardLightSrc : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrc Instance { get; } = new HardLightSrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.HardLightSrc(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrc(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(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.HardLightSrc(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(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.HardLightSrc(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(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.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrc(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.HardLightSrc(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.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalSrcAtop" composition equation. - /// - public sealed class NormalSrcAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcAtop Instance { get; } = new NormalSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(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.NormalSrcAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(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.NormalSrcAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(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.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(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.NormalSrcAtop(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.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrcAtop" composition equation. - /// - public sealed class MultiplySrcAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcAtop Instance { get; } = new MultiplySrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(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.MultiplySrcAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(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.MultiplySrcAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(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.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(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.MultiplySrcAtop(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.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrcAtop" composition equation. - /// - public sealed class AddSrcAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcAtop Instance { get; } = new AddSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.AddSrcAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(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.AddSrcAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(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.AddSrcAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(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.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcAtop(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.AddSrcAtop(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.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrcAtop" composition equation. - /// - public sealed class SubtractSrcAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcAtop Instance { get; } = new SubtractSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(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.SubtractSrcAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(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.SubtractSrcAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(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.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(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.SubtractSrcAtop(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.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrcAtop" composition equation. - /// - public sealed class ScreenSrcAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcAtop Instance { get; } = new ScreenSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(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.ScreenSrcAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(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.ScreenSrcAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(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.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(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.ScreenSrcAtop(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.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrcAtop" composition equation. - /// - public sealed class DarkenSrcAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcAtop Instance { get; } = new DarkenSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(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.DarkenSrcAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(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.DarkenSrcAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(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.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(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.DarkenSrcAtop(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.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrcAtop" composition equation. - /// - public sealed class LightenSrcAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcAtop Instance { get; } = new LightenSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(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.LightenSrcAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(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.LightenSrcAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(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.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(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.LightenSrcAtop(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.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrcAtop" composition equation. - /// - public sealed class OverlaySrcAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcAtop Instance { get; } = new OverlaySrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(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.OverlaySrcAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(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.OverlaySrcAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(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.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(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.OverlaySrcAtop(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.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrcAtop" composition equation. - /// - public sealed class HardLightSrcAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcAtop Instance { get; } = new HardLightSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(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.HardLightSrcAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(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.HardLightSrcAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(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.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(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.HardLightSrcAtop(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.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalSrcOver" composition equation. - /// - public sealed class NormalSrcOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcOver Instance { get; } = new NormalSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.NormalSrcOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(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.NormalSrcOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(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.NormalSrcOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(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.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOver(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.NormalSrcOver(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.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrcOver" composition equation. - /// - public sealed class MultiplySrcOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcOver Instance { get; } = new MultiplySrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(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.MultiplySrcOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(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.MultiplySrcOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(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.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(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.MultiplySrcOver(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.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrcOver" composition equation. - /// - public sealed class AddSrcOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcOver Instance { get; } = new AddSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.AddSrcOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(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.AddSrcOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(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.AddSrcOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(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.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOver(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.AddSrcOver(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.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrcOver" composition equation. - /// - public sealed class SubtractSrcOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcOver Instance { get; } = new SubtractSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(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.SubtractSrcOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(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.SubtractSrcOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(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.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(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.SubtractSrcOver(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.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrcOver" composition equation. - /// - public sealed class ScreenSrcOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcOver Instance { get; } = new ScreenSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(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.ScreenSrcOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(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.ScreenSrcOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(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.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(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.ScreenSrcOver(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.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrcOver" composition equation. - /// - public sealed class DarkenSrcOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcOver Instance { get; } = new DarkenSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(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.DarkenSrcOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(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.DarkenSrcOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(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.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(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.DarkenSrcOver(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.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrcOver" composition equation. - /// - public sealed class LightenSrcOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcOver Instance { get; } = new LightenSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.LightenSrcOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(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.LightenSrcOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(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.LightenSrcOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(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.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOver(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.LightenSrcOver(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.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrcOver" composition equation. - /// - public sealed class OverlaySrcOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcOver Instance { get; } = new OverlaySrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(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.OverlaySrcOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(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.OverlaySrcOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(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.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(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.OverlaySrcOver(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.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrcOver" composition equation. - /// - public sealed class HardLightSrcOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcOver Instance { get; } = new HardLightSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(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.HardLightSrcOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(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.HardLightSrcOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(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.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(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.HardLightSrcOver(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.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalSrcIn" composition equation. - /// - public sealed class NormalSrcIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcIn Instance { get; } = new NormalSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.NormalSrcIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(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.NormalSrcIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(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.NormalSrcIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(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.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcIn(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.NormalSrcIn(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.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrcIn" composition equation. - /// - public sealed class MultiplySrcIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcIn Instance { get; } = new MultiplySrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(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.MultiplySrcIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(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.MultiplySrcIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(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.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(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.MultiplySrcIn(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.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrcIn" composition equation. - /// - public sealed class AddSrcIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcIn Instance { get; } = new AddSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.AddSrcIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(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.AddSrcIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(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.AddSrcIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(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.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcIn(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.AddSrcIn(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.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrcIn" composition equation. - /// - public sealed class SubtractSrcIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcIn Instance { get; } = new SubtractSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(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.SubtractSrcIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(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.SubtractSrcIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(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.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(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.SubtractSrcIn(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.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrcIn" composition equation. - /// - public sealed class ScreenSrcIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcIn Instance { get; } = new ScreenSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(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.ScreenSrcIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(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.ScreenSrcIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(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.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(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.ScreenSrcIn(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.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrcIn" composition equation. - /// - public sealed class DarkenSrcIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcIn Instance { get; } = new DarkenSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(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.DarkenSrcIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(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.DarkenSrcIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(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.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(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.DarkenSrcIn(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.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrcIn" composition equation. - /// - public sealed class LightenSrcIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcIn Instance { get; } = new LightenSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.LightenSrcIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(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.LightenSrcIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(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.LightenSrcIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(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.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcIn(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.LightenSrcIn(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.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrcIn" composition equation. - /// - public sealed class OverlaySrcIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcIn Instance { get; } = new OverlaySrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(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.OverlaySrcIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(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.OverlaySrcIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(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.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(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.OverlaySrcIn(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.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrcIn" composition equation. - /// - public sealed class HardLightSrcIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcIn Instance { get; } = new HardLightSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(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.HardLightSrcIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(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.HardLightSrcIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(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.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(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.HardLightSrcIn(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.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalSrcOut" composition equation. - /// - public sealed class NormalSrcOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcOut Instance { get; } = new NormalSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.NormalSrcOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(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.NormalSrcOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(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.NormalSrcOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(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.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalSrcOut(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.NormalSrcOut(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.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrcOut" composition equation. - /// - public sealed class MultiplySrcOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcOut Instance { get; } = new MultiplySrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(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.MultiplySrcOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(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.MultiplySrcOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(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.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(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.MultiplySrcOut(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.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrcOut" composition equation. - /// - public sealed class AddSrcOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcOut Instance { get; } = new AddSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.AddSrcOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(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.AddSrcOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(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.AddSrcOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(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.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddSrcOut(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.AddSrcOut(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.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrcOut" composition equation. - /// - public sealed class SubtractSrcOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcOut Instance { get; } = new SubtractSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(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.SubtractSrcOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(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.SubtractSrcOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(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.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(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.SubtractSrcOut(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.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrcOut" composition equation. - /// - public sealed class ScreenSrcOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcOut Instance { get; } = new ScreenSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(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.ScreenSrcOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(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.ScreenSrcOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(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.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(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.ScreenSrcOut(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.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrcOut" composition equation. - /// - public sealed class DarkenSrcOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcOut Instance { get; } = new DarkenSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(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.DarkenSrcOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(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.DarkenSrcOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(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.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(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.DarkenSrcOut(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.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrcOut" composition equation. - /// - public sealed class LightenSrcOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcOut Instance { get; } = new LightenSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.LightenSrcOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(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.LightenSrcOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(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.LightenSrcOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(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.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenSrcOut(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.LightenSrcOut(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.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrcOut" composition equation. - /// - public sealed class OverlaySrcOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcOut Instance { get; } = new OverlaySrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(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.OverlaySrcOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(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.OverlaySrcOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(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.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(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.OverlaySrcOut(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.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrcOut" composition equation. - /// - public sealed class HardLightSrcOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcOut Instance { get; } = new HardLightSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(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.HardLightSrcOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(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.HardLightSrcOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(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.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(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.HardLightSrcOut(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.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDest" composition equation. - /// - public sealed class NormalDest : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDest Instance { get; } = new NormalDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.NormalDest(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(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.NormalDest(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(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.NormalDest(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(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.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDest(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.NormalDest(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.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDest" composition equation. - /// - public sealed class MultiplyDest : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDest Instance { get; } = new MultiplyDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.MultiplyDest(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(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.MultiplyDest(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(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.MultiplyDest(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(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.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDest(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.MultiplyDest(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.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDest" composition equation. - /// - public sealed class AddDest : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDest Instance { get; } = new AddDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.AddDest(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(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.AddDest(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(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.AddDest(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(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.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDest(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.AddDest(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.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDest" composition equation. - /// - public sealed class SubtractDest : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDest Instance { get; } = new SubtractDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.SubtractDest(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(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.SubtractDest(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(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.SubtractDest(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(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.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDest(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.SubtractDest(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.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDest" composition equation. - /// - public sealed class ScreenDest : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDest Instance { get; } = new ScreenDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.ScreenDest(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(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.ScreenDest(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(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.ScreenDest(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(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.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDest(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.ScreenDest(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.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDest" composition equation. - /// - public sealed class DarkenDest : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDest Instance { get; } = new DarkenDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.DarkenDest(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(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.DarkenDest(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(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.DarkenDest(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(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.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDest(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.DarkenDest(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.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDest" composition equation. - /// - public sealed class LightenDest : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDest Instance { get; } = new LightenDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.LightenDest(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(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.LightenDest(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(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.LightenDest(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(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.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDest(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.LightenDest(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.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDest" composition equation. - /// - public sealed class OverlayDest : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDest Instance { get; } = new OverlayDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.OverlayDest(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(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.OverlayDest(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(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.OverlayDest(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(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.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDest(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.OverlayDest(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.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDest" composition equation. - /// - public sealed class HardLightDest : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDest Instance { get; } = new HardLightDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.HardLightDest(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(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.HardLightDest(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(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.HardLightDest(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(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.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDest(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.HardLightDest(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.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDestAtop" composition equation. - /// - public sealed class NormalDestAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestAtop Instance { get; } = new NormalDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.NormalDestAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(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.NormalDestAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(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.NormalDestAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(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.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestAtop(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.NormalDestAtop(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.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDestAtop" composition equation. - /// - public sealed class MultiplyDestAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestAtop Instance { get; } = new MultiplyDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(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.MultiplyDestAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(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.MultiplyDestAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(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.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(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.MultiplyDestAtop(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.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestAtop" composition equation. - /// - public sealed class AddDestAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestAtop Instance { get; } = new AddDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.AddDestAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(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.AddDestAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(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.AddDestAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(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.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestAtop(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.AddDestAtop(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.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDestAtop" composition equation. - /// - public sealed class SubtractDestAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestAtop Instance { get; } = new SubtractDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(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.SubtractDestAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(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.SubtractDestAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(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.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(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.SubtractDestAtop(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.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDestAtop" composition equation. - /// - public sealed class ScreenDestAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestAtop Instance { get; } = new ScreenDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(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.ScreenDestAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(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.ScreenDestAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(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.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(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.ScreenDestAtop(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.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestAtop" composition equation. - /// - public sealed class DarkenDestAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestAtop Instance { get; } = new DarkenDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(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.DarkenDestAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(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.DarkenDestAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(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.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(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.DarkenDestAtop(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.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDestAtop" composition equation. - /// - public sealed class LightenDestAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestAtop Instance { get; } = new LightenDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.LightenDestAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(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.LightenDestAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(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.LightenDestAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(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.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestAtop(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.LightenDestAtop(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.LightenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDestAtop" composition equation. - /// - public sealed class OverlayDestAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestAtop Instance { get; } = new OverlayDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(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.OverlayDestAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(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.OverlayDestAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(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.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(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.OverlayDestAtop(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.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestAtop" composition equation. - /// - public sealed class HardLightDestAtop : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestAtop Instance { get; } = new HardLightDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(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.HardLightDestAtop(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(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.HardLightDestAtop(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(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.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(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.HardLightDestAtop(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.HardLightDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDestOver" composition equation. - /// - public sealed class NormalDestOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestOver Instance { get; } = new NormalDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.NormalDestOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(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.NormalDestOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(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.NormalDestOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(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.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOver(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.NormalDestOver(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.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDestOver" composition equation. - /// - public sealed class MultiplyDestOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestOver Instance { get; } = new MultiplyDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(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.MultiplyDestOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(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.MultiplyDestOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(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.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(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.MultiplyDestOver(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.MultiplyDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestOver" composition equation. - /// - public sealed class AddDestOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestOver Instance { get; } = new AddDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.AddDestOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(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.AddDestOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(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.AddDestOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(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.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestOver(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.AddDestOver(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.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDestOver" composition equation. - /// - public sealed class SubtractDestOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestOver Instance { get; } = new SubtractDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.SubtractDestOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(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.SubtractDestOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(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.SubtractDestOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(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.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOver(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.SubtractDestOver(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.SubtractDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDestOver" composition equation. - /// - public sealed class ScreenDestOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestOver Instance { get; } = new ScreenDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.ScreenDestOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(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.ScreenDestOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(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.ScreenDestOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(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.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOver(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.ScreenDestOver(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.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestOver" composition equation. - /// - public sealed class DarkenDestOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestOver Instance { get; } = new DarkenDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.DarkenDestOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(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.DarkenDestOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(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.DarkenDestOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(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.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOver(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.DarkenDestOver(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.DarkenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDestOver" composition equation. - /// - public sealed class LightenDestOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestOver Instance { get; } = new LightenDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.LightenDestOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(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.LightenDestOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(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.LightenDestOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(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.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOver(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.LightenDestOver(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.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDestOver" composition equation. - /// - public sealed class OverlayDestOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestOver Instance { get; } = new OverlayDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.OverlayDestOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(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.OverlayDestOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(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.OverlayDestOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(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.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOver(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.OverlayDestOver(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.OverlayDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestOver" composition equation. - /// - public sealed class HardLightDestOver : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestOver Instance { get; } = new HardLightDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.HardLightDestOver(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(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.HardLightDestOver(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(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.HardLightDestOver(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(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.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOver(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.HardLightDestOver(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.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDestIn" composition equation. - /// - public sealed class NormalDestIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestIn Instance { get; } = new NormalDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.NormalDestIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(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.NormalDestIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(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.NormalDestIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(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.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestIn(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.NormalDestIn(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.NormalDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDestIn" composition equation. - /// - public sealed class MultiplyDestIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestIn Instance { get; } = new MultiplyDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(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.MultiplyDestIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(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.MultiplyDestIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(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.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(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.MultiplyDestIn(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.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestIn" composition equation. - /// - public sealed class AddDestIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestIn Instance { get; } = new AddDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.AddDestIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(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.AddDestIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(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.AddDestIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(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.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestIn(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.AddDestIn(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.AddDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDestIn" composition equation. - /// - public sealed class SubtractDestIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestIn Instance { get; } = new SubtractDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.SubtractDestIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(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.SubtractDestIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(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.SubtractDestIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(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.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestIn(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.SubtractDestIn(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.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDestIn" composition equation. - /// - public sealed class ScreenDestIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestIn Instance { get; } = new ScreenDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.ScreenDestIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(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.ScreenDestIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(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.ScreenDestIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(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.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestIn(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.ScreenDestIn(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.ScreenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestIn" composition equation. - /// - public sealed class DarkenDestIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestIn Instance { get; } = new DarkenDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.DarkenDestIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(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.DarkenDestIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(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.DarkenDestIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(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.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestIn(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.DarkenDestIn(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.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDestIn" composition equation. - /// - public sealed class LightenDestIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestIn Instance { get; } = new LightenDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.LightenDestIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(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.LightenDestIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(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.LightenDestIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(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.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestIn(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.LightenDestIn(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.LightenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDestIn" composition equation. - /// - public sealed class OverlayDestIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestIn Instance { get; } = new OverlayDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.OverlayDestIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(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.OverlayDestIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(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.OverlayDestIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(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.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestIn(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.OverlayDestIn(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.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestIn" composition equation. - /// - public sealed class HardLightDestIn : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestIn Instance { get; } = new HardLightDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.HardLightDestIn(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(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.HardLightDestIn(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(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.HardLightDestIn(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(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.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestIn(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.HardLightDestIn(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.HardLightDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDestOut" composition equation. - /// - public sealed class NormalDestOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestOut Instance { get; } = new NormalDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.NormalDestOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(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.NormalDestOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(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.NormalDestOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(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.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalDestOut(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.NormalDestOut(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.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDestOut" composition equation. - /// - public sealed class MultiplyDestOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestOut Instance { get; } = new MultiplyDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(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.MultiplyDestOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(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.MultiplyDestOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(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.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(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.MultiplyDestOut(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.MultiplyDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestOut" composition equation. - /// - public sealed class AddDestOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestOut Instance { get; } = new AddDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.AddDestOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(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.AddDestOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(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.AddDestOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(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.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddDestOut(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.AddDestOut(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.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDestOut" composition equation. - /// - public sealed class SubtractDestOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestOut Instance { get; } = new SubtractDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.SubtractDestOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(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.SubtractDestOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(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.SubtractDestOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(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.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractDestOut(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.SubtractDestOut(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.SubtractDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDestOut" composition equation. - /// - public sealed class ScreenDestOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestOut Instance { get; } = new ScreenDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.ScreenDestOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(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.ScreenDestOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(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.ScreenDestOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(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.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenDestOut(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.ScreenDestOut(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.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestOut" composition equation. - /// - public sealed class DarkenDestOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestOut Instance { get; } = new DarkenDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.DarkenDestOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(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.DarkenDestOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(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.DarkenDestOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(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.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenDestOut(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.DarkenDestOut(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.DarkenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDestOut" composition equation. - /// - public sealed class LightenDestOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestOut Instance { get; } = new LightenDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.LightenDestOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(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.LightenDestOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(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.LightenDestOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(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.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenDestOut(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.LightenDestOut(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.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDestOut" composition equation. - /// - public sealed class OverlayDestOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestOut Instance { get; } = new OverlayDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.OverlayDestOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(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.OverlayDestOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(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.OverlayDestOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(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.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayDestOut(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.OverlayDestOut(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.OverlayDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestOut" composition equation. - /// - public sealed class HardLightDestOut : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestOut Instance { get; } = new HardLightDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.HardLightDestOut(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(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.HardLightDestOut(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(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.HardLightDestOut(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(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.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightDestOut(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.HardLightDestOut(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.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalClear" composition equation. - /// - public sealed class NormalClear : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalClear Instance { get; } = new NormalClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.NormalClear(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(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.NormalClear(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(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.NormalClear(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(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.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalClear(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.NormalClear(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.NormalClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyClear" composition equation. - /// - public sealed class MultiplyClear : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyClear Instance { get; } = new MultiplyClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.MultiplyClear(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(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.MultiplyClear(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(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.MultiplyClear(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(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.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyClear(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.MultiplyClear(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.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddClear" composition equation. - /// - public sealed class AddClear : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddClear Instance { get; } = new AddClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.AddClear(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(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.AddClear(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(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.AddClear(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(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.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddClear(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.AddClear(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.AddClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractClear" composition equation. - /// - public sealed class SubtractClear : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractClear Instance { get; } = new SubtractClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.SubtractClear(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(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.SubtractClear(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(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.SubtractClear(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(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.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractClear(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.SubtractClear(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.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenClear" composition equation. - /// - public sealed class ScreenClear : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenClear Instance { get; } = new ScreenClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.ScreenClear(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(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.ScreenClear(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(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.ScreenClear(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(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.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenClear(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.ScreenClear(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.ScreenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenClear" composition equation. - /// - public sealed class DarkenClear : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenClear Instance { get; } = new DarkenClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.DarkenClear(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(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.DarkenClear(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(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.DarkenClear(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(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.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenClear(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.DarkenClear(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.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenClear" composition equation. - /// - public sealed class LightenClear : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenClear Instance { get; } = new LightenClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.LightenClear(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(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.LightenClear(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(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.LightenClear(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(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.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenClear(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.LightenClear(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.LightenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayClear" composition equation. - /// - public sealed class OverlayClear : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayClear Instance { get; } = new OverlayClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.OverlayClear(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(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.OverlayClear(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(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.OverlayClear(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(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.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayClear(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.OverlayClear(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.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightClear" composition equation. - /// - public sealed class HardLightClear : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightClear Instance { get; } = new HardLightClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.HardLightClear(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(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.HardLightClear(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(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.HardLightClear(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(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.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightClear(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.HardLightClear(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.HardLightClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalXor" composition equation. - /// - public sealed class NormalXor : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalXor Instance { get; } = new NormalXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.NormalXor(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(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.NormalXor(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(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.NormalXor(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(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.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.NormalXor(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.NormalXor(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.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyXor" composition equation. - /// - public sealed class MultiplyXor : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyXor Instance { get; } = new MultiplyXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.MultiplyXor(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplyXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(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.MultiplyXor(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(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.MultiplyXor(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(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.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.MultiplyXor(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.MultiplyXor(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.MultiplyXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddXor" composition equation. - /// - public sealed class AddXor : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddXor Instance { get; } = new AddXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.AddXor(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(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.AddXor(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(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.AddXor(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(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.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.AddXor(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.AddXor(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.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractXor" composition equation. - /// - public sealed class SubtractXor : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractXor Instance { get; } = new SubtractXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.SubtractXor(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(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.SubtractXor(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(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.SubtractXor(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(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.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.SubtractXor(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.SubtractXor(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.SubtractXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenXor" composition equation. - /// - public sealed class ScreenXor : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenXor Instance { get; } = new ScreenXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.ScreenXor(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(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.ScreenXor(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(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.ScreenXor(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(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.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.ScreenXor(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.ScreenXor(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.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenXor" composition equation. - /// - public sealed class DarkenXor : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenXor Instance { get; } = new DarkenXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.DarkenXor(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.DarkenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(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.DarkenXor(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(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.DarkenXor(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(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.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.DarkenXor(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.DarkenXor(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.DarkenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenXor" composition equation. - /// - public sealed class LightenXor : AssociatedAlphaPixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenXor Instance { get; } = new LightenXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.LightenXor(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(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.LightenXor(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(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.LightenXor(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(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.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.LightenXor(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.LightenXor(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.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "AddDest" composition equation. + /// + public sealed class AddDest : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddDest Instance { get; } = new(); } /// - /// A pixel blender that implements the "OverlayXor" composition equation. + /// Blends pixels with the "SubtractDest" composition equation. /// - public sealed class OverlayXor : AssociatedAlphaPixelBlender + public sealed class SubtractDest : + AssociatedAlphaPixelBlender { /// - /// Gets the static instance of this blender. + /// Gets the shared blender instance. /// - public static OverlayXor Instance { get; } = new OverlayXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.OverlayXor(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.OverlayXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(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.OverlayXor(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(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.OverlayXor(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(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.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = AssociatedAlphaPorterDuffFunctions.OverlayXor(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.OverlayXor(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.OverlayXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + public static SubtractDest Instance { get; } = new(); } /// - /// A pixel blender that implements the "HardLightXor" composition equation. + /// Blends pixels with the "ScreenDest" composition equation. /// - public sealed class HardLightXor : AssociatedAlphaPixelBlender + public sealed class ScreenDest : + AssociatedAlphaPixelBlender { /// - /// Gets the static instance of this blender. + /// Gets the shared blender instance. /// - public static HardLightXor Instance { get; } = new HardLightXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.HardLightXor(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightXor(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.HardLightXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightXor(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.HardLightXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightXor(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.HardLightXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightXor(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.HardLightXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightXor(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.HardLightXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightXor(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.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightXor(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.HardLightXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + public static ScreenDest Instance { get; } = new(); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "DarkenDest" composition equation. + /// + public sealed class DarkenDest : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenDest Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Blends pixels with the "LightenDest" composition equation. + /// + public sealed class LightenDest : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenDest Instance { get; } = new(); + } - destinationBase = AssociatedAlphaPorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } + /// + /// Blends pixels with the "OverlayDest" composition equation. + /// + public sealed class OverlayDest : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayDest Instance { get; } = new(); + } - 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.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = AssociatedAlphaPorterDuffFunctions.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "HardLightDest" composition equation. + /// + public sealed class HardLightDest : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightDest Instance { get; } = new(); + } - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + /// + /// Blends pixels with the "NormalDestAtop" composition equation. + /// + public sealed class NormalDestAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalDestAtop Instance { get; } = new(); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "MultiplyDestAtop" composition equation. + /// + public sealed class MultiplyDestAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyDestAtop Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + /// Blends pixels with the "AddDestAtop" composition equation. + /// + public sealed class AddDestAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddDestAtop Instance { get; } = new(); + } - 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); + /// + /// Blends pixels with the "SubtractDestAtop" composition equation. + /// + public sealed class SubtractDestAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractDestAtop Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "ScreenDestAtop" composition equation. + /// + public sealed class ScreenDestAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenDestAtop Instance { get; } = new(); + } - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightXor(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); - } + /// + /// Blends pixels with the "DarkenDestAtop" composition equation. + /// + public sealed class DarkenDestAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenDestAtop Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.HardLightXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "LightenDestAtop" composition equation. + /// + public sealed class LightenDestAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenDestAtop Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "OverlayDestAtop" composition equation. + /// + public sealed class OverlayDestAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayDestAtop Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "HardLightDestAtop" composition equation. + /// + public sealed class HardLightDestAtop : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightDestAtop Instance { get; } = new(); + } - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightXor(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); - } + /// + /// Blends pixels with the "NormalDestOver" composition equation. + /// + public sealed class NormalDestOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalDestOver Instance { get; } = new(); + } - 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.HardLightXor(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.HardLightXor(background[i], source[i], amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "MultiplyDestOver" composition equation. + /// + public sealed class MultiplyDestOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyDestOver Instance { get; } = new(); + } - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + /// + /// Blends pixels with the "AddDestOver" composition equation. + /// + public sealed class AddDestOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddDestOver Instance { get; } = new(); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "SubtractDestOver" composition equation. + /// + public sealed class SubtractDestOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractDestOver Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "ScreenDestOver" composition equation. + /// + public sealed class ScreenDestOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenDestOver Instance { get; } = new(); + } - Vector512 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 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + /// Blends pixels with the "DarkenDestOver" composition equation. + /// + public sealed class DarkenDestOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenDestOver Instance { get; } = new(); + } - 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); + /// + /// Blends pixels with the "LightenDestOver" composition equation. + /// + public sealed class LightenDestOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenDestOver Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "OverlayDestOver" composition equation. + /// + public sealed class OverlayDestOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayDestOver Instance { get; } = new(); + } - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightXor(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); - } + /// + /// Blends pixels with the "HardLightDestOver" composition equation. + /// + public sealed class HardLightDestOver : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightDestOver Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.HardLightXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "NormalDestIn" composition equation. + /// + public sealed class NormalDestIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalDestIn Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "MultiplyDestIn" composition equation. + /// + public sealed class MultiplyDestIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyDestIn Instance { get; } = new(); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "AddDestIn" composition equation. + /// + public sealed class AddDestIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddDestIn Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "SubtractDestIn" composition equation. + /// + public sealed class SubtractDestIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractDestIn Instance { get; } = new(); + } - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightXor(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); - } + /// + /// Blends pixels with the "ScreenDestIn" composition equation. + /// + public sealed class ScreenDestIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenDestIn Instance { get; } = new(); + } - 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.HardLightXor(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.HardLightXor(background[i], source, amount); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "DarkenDestIn" composition equation. + /// + public sealed class DarkenDestIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenDestIn Instance { get; } = new(); + } - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "LightenDestIn" composition equation. + /// + public sealed class LightenDestIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenDestIn Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "OverlayDestIn" composition equation. + /// + public sealed class OverlayDestIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayDestIn Instance { get; } = new(); + } - Vector512 vOne = Vector512.Create(1F); + /// + /// Blends pixels with the "HardLightDestIn" composition equation. + /// + public sealed class HardLightDestIn : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightDestIn Instance { get; } = new(); + } - 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); + /// + /// Blends pixels with the "NormalDestOut" composition equation. + /// + public sealed class NormalDestOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalDestOut Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + /// Blends pixels with the "MultiplyDestOut" composition equation. + /// + public sealed class MultiplyDestOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyDestOut Instance { get; } = new(); + } - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + /// Blends pixels with the "AddDestOut" composition equation. + /// + public sealed class AddDestOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddDestOut Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "SubtractDestOut" composition equation. + /// + public sealed class SubtractDestOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractDestOut Instance { get; } = new(); + } - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightXor(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); - } + /// + /// Blends pixels with the "ScreenDestOut" composition equation. + /// + public sealed class ScreenDestOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenDestOut Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.HardLightXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "DarkenDestOut" composition equation. + /// + public sealed class DarkenDestOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenDestOut Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "LightenDestOut" composition equation. + /// + public sealed class LightenDestOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenDestOut Instance { get; } = new(); + } - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "OverlayDestOut" composition equation. + /// + public sealed class OverlayDestOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayDestOut Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Blends pixels with the "HardLightDestOut" composition equation. + /// + public sealed class HardLightDestOut : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightDestOut Instance { get; } = new(); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "NormalClear" composition equation. + /// + public sealed class NormalClear : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalClear Instance { get; } = new(); + } - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightXor(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); - } + /// + /// Blends pixels with the "MultiplyClear" composition equation. + /// + public sealed class MultiplyClear : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyClear Instance { get; } = new(); + } - 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.HardLightXor(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.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "AddClear" composition equation. + /// + public sealed class AddClear : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddClear Instance { get; } = new(); + } - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "SubtractClear" composition equation. + /// + public sealed class SubtractClear : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractClear Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "ScreenClear" composition equation. + /// + public sealed class ScreenClear : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenClear Instance { get; } = new(); + } - Vector512 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 vOne = Vector512.Create(1F); + /// + /// Blends pixels with the "DarkenClear" composition equation. + /// + public sealed class DarkenClear : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenClear Instance { get; } = new(); + } - 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); + /// + /// Blends pixels with the "LightenClear" composition equation. + /// + public sealed class LightenClear : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenClear Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + /// Blends pixels with the "OverlayClear" composition equation. + /// + public sealed class OverlayClear : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayClear Instance { get; } = new(); + } - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + /// Blends pixels with the "HardLightClear" composition equation. + /// + public sealed class HardLightClear : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightClear Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "NormalXor" composition equation. + /// + public sealed class NormalXor : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalXor Instance { get; } = new(); + } - Vector512 blended = AssociatedAlphaPorterDuffFunctions.HardLightXor(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); - } + /// + /// Blends pixels with the "MultiplyXor" composition equation. + /// + public sealed class MultiplyXor : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyXor Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = AssociatedAlphaPorterDuffFunctions.HardLightXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "AddXor" composition equation. + /// + public sealed class AddXor : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddXor Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "SubtractXor" composition equation. + /// + public sealed class SubtractXor : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractXor Instance { get; } = new(); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "ScreenXor" composition equation. + /// + public sealed class ScreenXor : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenXor Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Blends pixels with the "DarkenXor" composition equation. + /// + public sealed class DarkenXor : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenXor Instance { get; } = new(); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "LightenXor" composition equation. + /// + public sealed class LightenXor : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenXor Instance { get; } = new(); + } - Vector256 blended = AssociatedAlphaPorterDuffFunctions.HardLightXor(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); - } + /// + /// Blends pixels with the "OverlayXor" composition equation. + /// + public sealed class OverlayXor : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayXor Instance { get; } = new(); + } - 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.HardLightXor(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.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "HardLightXor" composition equation. + /// + public sealed class HardLightXor : + AssociatedAlphaPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightXor Instance { get; } = new(); } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt index 69426b729..70607feb1 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt @@ -4,31 +4,19 @@ #> <#@ 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. // 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; -/// -/// Provides generated Porter-Duff blenders for associated-alpha pixel formats. -/// -internal static partial class AssociatedAlphaPixelBlenders - where TPixel : unmanaged, IPixel -{ - <# -var composers = new []{ +var composers = new [] +{ "Src", "SrcAtop", "SrcOver", @@ -43,7 +31,8 @@ var composers = new []{ "Xor", }; -var blenders = new []{ +var blenders = new [] +{ "Normal", "Multiply", "Add", @@ -52,796 +41,79 @@ var blenders = new []{ "Darken", "Lighten", "Overlay", - "HardLight" + "HardLight", }; - - foreach(var composer in composers) { - foreach(var blender in blenders) { - - var blender_composer= $"{blender}{composer}"; +#> +/// +/// Provides the associated-alpha equations consumed by the shared pixel-blending traversal. +/// +internal static class AssociatedAlphaPixelBlenderOperators +{ +<# +foreach (var composer in composers) +{ + foreach (var blender in blenders) + { + var blenderComposer = $"{blender}{composer}"; #> /// - /// A pixel blender that implements the "<#= blender_composer#>" composition equation. + /// Applies the "<#= blenderComposer #>" associated-alpha composition equation. /// - public sealed class <#= blender_composer#> : AssociatedAlphaPixelBlender + public readonly struct <#= blenderComposer #> : IPixelBlenderOperator { - /// - /// Gets the static instance of this blender. - /// - public static <#=blender_composer#> Instance { get; } = new <#=blender_composer#>(); - /// - 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))); - } + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.<#= blenderComposer #>(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 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); - } - } - } + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.<#= blenderComposer #>(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 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); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.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)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.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)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 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)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 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)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 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)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.<#= blenderComposer #>(background, source, amount); + } - Vector256 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)); - } - } - } +/// +/// Provides associated-alpha pixel blenders for each color and alpha composition pair. +/// +/// The associated-alpha destination pixel format. +internal static partial class AssociatedAlphaPixelBlenders + where TPixel : unmanaged, IPixel +{ +<# +foreach (var composer in composers) +{ + foreach (var blender in blenders) + { + var blenderComposer = $"{blender}{composer}"; +#> + /// + /// Blends pixels with the "<#= blenderComposer #>" composition equation. + /// + public sealed class <#= blenderComposer #> : + AssociatedAlphaPixelBlender> + { + /// + /// Gets the shared blender instance. + /// + public static <#= blenderComposer #> Instance { get; } = new(); } <# } } - #> } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlender{TPixel,TOperator}.cs similarity index 67% rename from src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlender{TPixel}.cs rename to src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlender{TPixel,TOperator}.cs index 9c71e97e5..00427308c 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlender{TPixel,TOperator}.cs @@ -9,11 +9,25 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; /// Provides the vector representation used to blend pixels that store associated alpha. /// /// The associated-alpha pixel format. -internal abstract class AssociatedAlphaPixelBlender : PixelBlender +/// The Porter-Duff equation. +internal abstract class AssociatedAlphaPixelBlender : PixelBlender where TPixel : unmanaged, IPixel + where TOperator : struct, IPixelBlenderOperator { private static readonly PixelOperations Operations = PixelOperations.Instance; + /// + public sealed override TPixel Blend(TPixel background, TPixel source, float amount) + { + // Associated RGB and alpha must remain in their stored representation throughout composition. + Vector4 result = TOperator.Invoke( + background.ToAssociatedScaledVector4(), + source.ToAssociatedScaledVector4(), + Numerics.Clamp(amount, 0, 1F)); + + return TPixel.FromAssociatedScaledVector4(result); + } + /// protected override void ToBlendVector4( Configuration configuration, diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 983508d2d..250d789dd 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -3,84158 +3,3910 @@ // 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; /// -/// Collection of Porter Duff alpha blending functions applying different composition models. +/// Provides the straight-alpha equations consumed by the shared pixel-blending traversal. /// -/// -/// These functions are designed to be a general solution for all color cases, -/// that is, they take in account the alpha value of both the backdrop -/// and source, and there's no need to alpha-premultiply neither the backdrop -/// nor the source. -/// Note there are faster functions for when the backdrop color is known -/// to be opaque -/// -internal static class DefaultPixelBlenders - where TPixel : unmanaged, IPixel +internal static class DefaultPixelBlenderOperators { - /// - /// A pixel blender that implements the "NormalSrc" composition equation. + /// Applies the "NormalSrc" straight-alpha composition equation. /// - public class NormalSrc : PixelBlender + public readonly struct NormalSrc : IPixelBlenderOperator { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrc Instance { get; } = new NormalSrc(); - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalSrc(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.NormalSrc(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.NormalSrc(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "MultiplySrc" straight-alpha composition equation. + /// + public readonly struct MultiplySrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplySrc(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.MultiplySrc(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.MultiplySrc(background, source, amount); + } - 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] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); - } - } - } + /// + /// Applies the "AddSrc" straight-alpha composition equation. + /// + public readonly struct AddSrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddSrc(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.AddSrc(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.AddSrc(background, source, amount); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); + /// + /// Applies the "SubtractSrc" straight-alpha composition equation. + /// + public readonly struct SubtractSrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractSrc(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.SubtractSrc(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.SubtractSrc(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); + /// + /// Applies the "ScreenSrc" straight-alpha composition equation. + /// + public readonly struct ScreenSrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenSrc(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.ScreenSrc(background, source, amount); - 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] = PorterDuffFunctions.NormalSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source, amount); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.ScreenSrc(background, source, amount); + } + /// + /// Applies the "DarkenSrc" straight-alpha composition equation. + /// + public readonly struct DarkenSrc : IPixelBlenderOperator + { /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenSrc(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.DarkenSrc(background, source, amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.DarkenSrc(background, source, amount); + } - 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); + /// + /// Applies the "LightenSrc" straight-alpha composition equation. + /// + public readonly struct LightenSrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenSrc(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.LightenSrc(background, source, amount); - destinationBase = PorterDuffFunctions.NormalSrc(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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.LightenSrc(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "OverlaySrc" straight-alpha composition equation. + /// + public readonly struct OverlaySrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlaySrc(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.OverlaySrc(background, source, amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.OverlaySrc(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Applies the "HardLightSrc" straight-alpha composition equation. + /// + public readonly struct HardLightSrc : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightSrc(background, source, amount); - destinationBase = PorterDuffFunctions.NormalSrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.HardLightSrc(background, source, amount); - 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] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.HardLightSrc(background, source, amount); + } + /// + /// Applies the "NormalSrcAtop" straight-alpha composition equation. + /// + public readonly struct NormalSrcAtop : IPixelBlenderOperator + { /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalSrcAtop(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.NormalSrcAtop(background, source, amount); - Vector512 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 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.NormalSrcAtop(background, source, amount); + } - 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); + /// + /// Applies the "MultiplySrcAtop" straight-alpha composition equation. + /// + public readonly struct MultiplySrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplySrcAtop(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.MultiplySrcAtop(background, source, amount); - destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 4); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.MultiplySrcAtop(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "AddSrcAtop" straight-alpha composition equation. + /// + public readonly struct AddSrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddSrcAtop(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.AddSrcAtop(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.AddSrcAtop(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Applies the "SubtractSrcAtop" straight-alpha composition equation. + /// + public readonly struct SubtractSrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractSrcAtop(background, source, amount); - destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.SubtractSrcAtop(background, source, amount); - 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] = PorterDuffFunctions.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.SubtractSrcAtop(background, source, amount); + } + /// + /// Applies the "ScreenSrcAtop" straight-alpha composition equation. + /// + public readonly struct ScreenSrcAtop : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenSrcAtop(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.ScreenSrcAtop(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.ScreenSrcAtop(background, source, amount); + } - 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); + /// + /// Applies the "DarkenSrcAtop" straight-alpha composition equation. + /// + public readonly struct DarkenSrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenSrcAtop(background, source, amount); - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.DarkenSrcAtop(background, source, amount); - Vector512 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.DarkenSrcAtop(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "LightenSrcAtop" straight-alpha composition equation. + /// + public readonly struct LightenSrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenSrcAtop(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.LightenSrcAtop(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.LightenSrcAtop(background, source, amount); + } - Vector256 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Applies the "OverlaySrcAtop" straight-alpha composition equation. + /// + public readonly struct OverlaySrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlaySrcAtop(background, source, amount); - 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 = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.OverlaySrcAtop(background, source, amount); /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.OverlaySrcAtop(background, source, amount); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Applies the "HardLightSrcAtop" straight-alpha composition equation. + /// + public readonly struct HardLightSrcAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightSrcAtop(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.HardLightSrcAtop(background, source, amount); - Vector512 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 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.HardLightSrcAtop(background, source, amount); + } - 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); + /// + /// Applies the "NormalSrcOver" straight-alpha composition equation. + /// + public readonly struct NormalSrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalSrcOver(background, source, amount); - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.NormalSrcOver(background, source, amount); - Vector512 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.NormalSrcOver(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "MultiplySrcOver" straight-alpha composition equation. + /// + public readonly struct MultiplySrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplySrcOver(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.MultiplySrcOver(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.MultiplySrcOver(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "AddSrcOver" straight-alpha composition equation. + /// + public readonly struct AddSrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddSrcOver(background, source, amount); - Vector256 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.AddSrcOver(background, source, amount); - 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 = PorterDuffFunctions.NormalSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.AddSrcOver(background, source, amount); + } + /// + /// Applies the "SubtractSrcOver" straight-alpha composition equation. + /// + public readonly struct SubtractSrcOver : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractSrcOver(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.SubtractSrcOver(background, source, amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.SubtractSrcOver(background, source, amount); + } - 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); + /// + /// Applies the "ScreenSrcOver" straight-alpha composition equation. + /// + public readonly struct ScreenSrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenSrcOver(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.ScreenSrcOver(background, source, amount); - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.ScreenSrcOver(background, source, amount); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Applies the "DarkenSrcOver" straight-alpha composition equation. + /// + public readonly struct DarkenSrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenSrcOver(background, source, amount); - Vector512 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.DarkenSrcOver(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.DarkenSrcOver(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Applies the "LightenSrcOver" straight-alpha composition equation. + /// + public readonly struct LightenSrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenSrcOver(background, source, amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.LightenSrcOver(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.LightenSrcOver(background, source, amount); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "OverlaySrcOver" straight-alpha composition equation. + /// + public readonly struct OverlaySrcOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlaySrcOver(background, source, amount); - Vector256 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.OverlaySrcOver(background, source, amount); - 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 = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.OverlaySrcOver(background, source, amount); + } + /// + /// Applies the "HardLightSrcOver" straight-alpha composition equation. + /// + public readonly struct HardLightSrcOver : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightSrcOver(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.HardLightSrcOver(background, source, amount); - Vector512 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 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.HardLightSrcOver(background, source, amount); + } - 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); + /// + /// Applies the "NormalSrcIn" straight-alpha composition equation. + /// + public readonly struct NormalSrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalSrcIn(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.NormalSrcIn(background, source, amount); - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.NormalSrcIn(background, source, amount); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Applies the "MultiplySrcIn" straight-alpha composition equation. + /// + public readonly struct MultiplySrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplySrcIn(background, source, amount); - Vector512 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.MultiplySrcIn(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.MultiplySrcIn(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Applies the "AddSrcIn" straight-alpha composition equation. + /// + public readonly struct AddSrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddSrcIn(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.AddSrcIn(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.AddSrcIn(background, source, amount); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "SubtractSrcIn" straight-alpha composition equation. + /// + public readonly struct SubtractSrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractSrcIn(background, source, amount); - Vector256 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.SubtractSrcIn(background, source, amount); - 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 = PorterDuffFunctions.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.SubtractSrcIn(background, source, amount); } /// - /// A pixel blender that implements the "MultiplySrc" composition equation. + /// Applies the "ScreenSrcIn" straight-alpha composition equation. /// - public class MultiplySrc : PixelBlender + public readonly struct ScreenSrcIn : IPixelBlenderOperator { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrc Instance { get; } = new MultiplySrc(); + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenSrcIn(background, source, amount); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.ScreenSrcIn(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.ScreenSrcIn(background, source, amount); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Applies the "DarkenSrcIn" straight-alpha composition equation. + /// + public readonly struct DarkenSrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenSrcIn(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.DarkenSrcIn(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.DarkenSrcIn(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "LightenSrcIn" straight-alpha composition equation. + /// + public readonly struct LightenSrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenSrcIn(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.LightenSrcIn(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.LightenSrcIn(background, source, amount); + } - 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] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); - } - } - } + /// + /// Applies the "OverlaySrcIn" straight-alpha composition equation. + /// + public readonly struct OverlaySrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlaySrcIn(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.OverlaySrcIn(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.OverlaySrcIn(background, source, amount); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); + /// + /// Applies the "HardLightSrcIn" straight-alpha composition equation. + /// + public readonly struct HardLightSrcIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightSrcIn(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.HardLightSrcIn(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.HardLightSrcIn(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); + /// + /// Applies the "NormalSrcOut" straight-alpha composition equation. + /// + public readonly struct NormalSrcOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalSrcOut(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.NormalSrcOut(background, source, amount); - 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] = PorterDuffFunctions.MultiplySrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, amount); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.NormalSrcOut(background, source, amount); + } + /// + /// Applies the "MultiplySrcOut" straight-alpha composition equation. + /// + public readonly struct MultiplySrcOut : IPixelBlenderOperator + { /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplySrcOut(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.MultiplySrcOut(background, source, amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.MultiplySrcOut(background, source, amount); + } - 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrc(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] = PorterDuffFunctions.MultiplySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "AddSrcOut" straight-alpha composition equation. + /// + public readonly struct AddSrcOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddSrcOut(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.AddSrcOut(background, source, amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.AddSrcOut(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Applies the "SubtractSrcOut" straight-alpha composition equation. + /// + public readonly struct SubtractSrcOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractSrcOut(background, source, amount); - destinationBase = PorterDuffFunctions.MultiplySrc(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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.SubtractSrcOut(background, source, amount); - 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] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.SubtractSrcOut(background, source, amount); + } + /// + /// Applies the "ScreenSrcOut" straight-alpha composition equation. + /// + public readonly struct ScreenSrcOut : IPixelBlenderOperator + { /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenSrcOut(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.ScreenSrcOut(background, source, amount); - Vector512 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 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.ScreenSrcOut(background, source, amount); + } - 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); + /// + /// Applies the "DarkenSrcOut" straight-alpha composition equation. + /// + public readonly struct DarkenSrcOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenSrcOut(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.DarkenSrcOut(background, source, amount); - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 4); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.DarkenSrcOut(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "LightenSrcOut" straight-alpha composition equation. + /// + public readonly struct LightenSrcOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenSrcOut(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.LightenSrcOut(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.LightenSrcOut(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Applies the "OverlaySrcOut" straight-alpha composition equation. + /// + public readonly struct OverlaySrcOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlaySrcOut(background, source, amount); - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.OverlaySrcOut(background, source, amount); - 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] = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.OverlaySrcOut(background, source, amount); + } + /// + /// Applies the "HardLightSrcOut" straight-alpha composition equation. + /// + public readonly struct HardLightSrcOut : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightSrcOut(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.HardLightSrcOut(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.HardLightSrcOut(background, source, amount); + } - 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); + /// + /// Applies the "NormalDest" straight-alpha composition equation. + /// + public readonly struct NormalDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalDest(background, source, amount); - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.NormalDest(background, source, amount); - Vector512 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.NormalDest(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "MultiplyDest" straight-alpha composition equation. + /// + public readonly struct MultiplyDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyDest(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.MultiplyDest(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.MultiplyDest(background, source, amount); + } - Vector256 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Applies the "AddDest" straight-alpha composition equation. + /// + public readonly struct AddDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddDest(background, source, amount); - 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 = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.AddDest(background, source, amount); /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.AddDest(background, source, amount); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Applies the "SubtractDest" straight-alpha composition equation. + /// + public readonly struct SubtractDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractDest(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.SubtractDest(background, source, amount); - Vector512 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 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.SubtractDest(background, source, amount); + } - 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); + /// + /// Applies the "ScreenDest" straight-alpha composition equation. + /// + public readonly struct ScreenDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenDest(background, source, amount); - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.ScreenDest(background, source, amount); - Vector512 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.ScreenDest(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "DarkenDest" straight-alpha composition equation. + /// + public readonly struct DarkenDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenDest(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.DarkenDest(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.DarkenDest(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "LightenDest" straight-alpha composition equation. + /// + public readonly struct LightenDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenDest(background, source, amount); - Vector256 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.LightenDest(background, source, amount); - 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 = PorterDuffFunctions.MultiplySrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.LightenDest(background, source, amount); + } + /// + /// Applies the "OverlayDest" straight-alpha composition equation. + /// + public readonly struct OverlayDest : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayDest(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.OverlayDest(background, source, amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.OverlayDest(background, source, amount); + } - 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); + /// + /// Applies the "HardLightDest" straight-alpha composition equation. + /// + public readonly struct HardLightDest : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightDest(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.HardLightDest(background, source, amount); - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.HardLightDest(background, source, amount); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Applies the "NormalDestAtop" straight-alpha composition equation. + /// + public readonly struct NormalDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalDestAtop(background, source, amount); - Vector512 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.NormalDestAtop(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.NormalDestAtop(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Applies the "MultiplyDestAtop" straight-alpha composition equation. + /// + public readonly struct MultiplyDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyDestAtop(background, source, amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.MultiplyDestAtop(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.MultiplyDestAtop(background, source, amount); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "AddDestAtop" straight-alpha composition equation. + /// + public readonly struct AddDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddDestAtop(background, source, amount); - Vector256 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.AddDestAtop(background, source, amount); - 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 = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.AddDestAtop(background, source, amount); + } + /// + /// Applies the "SubtractDestAtop" straight-alpha composition equation. + /// + public readonly struct SubtractDestAtop : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractDestAtop(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.SubtractDestAtop(background, source, amount); - Vector512 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 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.SubtractDestAtop(background, source, amount); + } - 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); + /// + /// Applies the "ScreenDestAtop" straight-alpha composition equation. + /// + public readonly struct ScreenDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenDestAtop(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.ScreenDestAtop(background, source, amount); - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.ScreenDestAtop(background, source, amount); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Applies the "DarkenDestAtop" straight-alpha composition equation. + /// + public readonly struct DarkenDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenDestAtop(background, source, amount); - Vector512 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.DarkenDestAtop(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.DarkenDestAtop(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Applies the "LightenDestAtop" straight-alpha composition equation. + /// + public readonly struct LightenDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenDestAtop(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.LightenDestAtop(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.LightenDestAtop(background, source, amount); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "OverlayDestAtop" straight-alpha composition equation. + /// + public readonly struct OverlayDestAtop : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayDestAtop(background, source, amount); - Vector256 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.OverlayDestAtop(background, source, amount); - 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 = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.OverlayDestAtop(background, source, amount); } /// - /// A pixel blender that implements the "AddSrc" composition equation. + /// Applies the "HardLightDestAtop" straight-alpha composition equation. /// - public class AddSrc : PixelBlender + public readonly struct HardLightDestAtop : IPixelBlenderOperator { - /// - /// Gets the static instance of this blender. - /// - public static AddSrc Instance { get; } = new AddSrc(); - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightDestAtop(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.HardLightDestAtop(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.HardLightDestAtop(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + /// Applies the "NormalDestOver" straight-alpha composition equation. + /// + public readonly struct NormalDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalDestOver(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.NormalDestOver(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.NormalDestOver(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + /// Applies the "MultiplyDestOver" straight-alpha composition equation. + /// + public readonly struct MultiplyDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyDestOver(background, source, amount); - 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] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); - } - } - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.MultiplyDestOver(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.MultiplyDestOver(background, source, amount); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Applies the "AddDestOver" straight-alpha composition equation. + /// + public readonly struct AddDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddDestOver(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.AddDestOver(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.AddDestOver(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "SubtractDestOver" straight-alpha composition equation. + /// + public readonly struct SubtractDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractDestOver(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.SubtractDestOver(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.SubtractDestOver(background, source, amount); + } - 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] = PorterDuffFunctions.AddSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source, amount); - } - } - } + /// + /// Applies the "ScreenDestOver" straight-alpha composition equation. + /// + public readonly struct ScreenDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenDestOver(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.ScreenDestOver(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.ScreenDestOver(background, source, amount); + } - Vector512 vOne = Vector512.Create(1F); + /// + /// Applies the "DarkenDestOver" straight-alpha composition equation. + /// + public readonly struct DarkenDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenDestOver(background, source, amount); - 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); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.DarkenDestOver(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.DarkenDestOver(background, source, amount); + } - destinationBase = PorterDuffFunctions.AddSrc(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); - } + /// + /// Applies the "LightenDestOver" straight-alpha composition equation. + /// + public readonly struct LightenDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenDestOver(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.LightenDestOver(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.LightenDestOver(background, source, amount); + } - Vector256 vOne = Vector256.Create(1F); + /// + /// Applies the "OverlayDestOver" straight-alpha composition equation. + /// + public readonly struct OverlayDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayDestOver(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.OverlayDestOver(background, source, amount); - destinationBase = PorterDuffFunctions.AddSrc(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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.OverlayDestOver(background, source, amount); + } - 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] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + /// Applies the "HardLightDestOver" straight-alpha composition equation. + /// + public readonly struct HardLightDestOver : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightDestOver(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.HardLightDestOver(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.HardLightDestOver(background, source, amount); + } - Vector512 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 vOne = Vector512.Create(1F); + /// + /// Applies the "NormalDestIn" straight-alpha composition equation. + /// + public readonly struct NormalDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalDestIn(background, source, amount); - 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); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.NormalDestIn(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.NormalDestIn(background, source, amount); + } - destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 4); - } + /// + /// Applies the "MultiplyDestIn" straight-alpha composition equation. + /// + public readonly struct MultiplyDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyDestIn(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.MultiplyDestIn(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.MultiplyDestIn(background, source, amount); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + /// Applies the "AddDestIn" straight-alpha composition equation. + /// + public readonly struct AddDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddDestIn(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.AddDestIn(background, source, amount); - destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.AddDestIn(background, source, amount); + } - 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] = PorterDuffFunctions.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + /// Applies the "SubtractDestIn" straight-alpha composition equation. + /// + public readonly struct SubtractDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractDestIn(background, source, amount); /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.SubtractDestIn(background, source, amount); /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.SubtractDestIn(background, source, amount); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Applies the "ScreenDestIn" straight-alpha composition equation. + /// + public readonly struct ScreenDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenDestIn(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.ScreenDestIn(background, source, amount); - Vector512 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 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.ScreenDestIn(background, source, amount); + } - 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); + /// + /// Applies the "DarkenDestIn" straight-alpha composition equation. + /// + public readonly struct DarkenDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenDestIn(background, source, amount); - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.DarkenDestIn(background, source, amount); - Vector512 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.DarkenDestIn(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "LightenDestIn" straight-alpha composition equation. + /// + public readonly struct LightenDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenDestIn(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.LightenDestIn(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.LightenDestIn(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "OverlayDestIn" straight-alpha composition equation. + /// + public readonly struct OverlayDestIn : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayDestIn(background, source, amount); - Vector256 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.OverlayDestIn(background, source, amount); - 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 = PorterDuffFunctions.AddSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.OverlayDestIn(background, source, amount); + } + /// + /// Applies the "HardLightDestIn" straight-alpha composition equation. + /// + public readonly struct HardLightDestIn : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightDestIn(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.HardLightDestIn(background, source, amount); - Vector512 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.HardLightDestIn(background, source, amount); + } - 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); + /// + /// Applies the "NormalDestOut" straight-alpha composition equation. + /// + public readonly struct NormalDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalDestOut(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.NormalDestOut(background, source, amount); - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.NormalDestOut(background, source, amount); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Applies the "MultiplyDestOut" straight-alpha composition equation. + /// + public readonly struct MultiplyDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyDestOut(background, source, amount); - Vector512 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.MultiplyDestOut(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.MultiplyDestOut(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Applies the "AddDestOut" straight-alpha composition equation. + /// + public readonly struct AddDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddDestOut(background, source, amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.AddDestOut(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.AddDestOut(background, source, amount); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "SubtractDestOut" straight-alpha composition equation. + /// + public readonly struct SubtractDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractDestOut(background, source, amount); - Vector256 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.SubtractDestOut(background, source, amount); - 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 = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.SubtractDestOut(background, source, amount); + } + /// + /// Applies the "ScreenDestOut" straight-alpha composition equation. + /// + public readonly struct ScreenDestOut : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenDestOut(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.ScreenDestOut(background, source, amount); - Vector512 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 vOne = Vector512.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.ScreenDestOut(background, source, amount); + } - 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); + /// + /// Applies the "DarkenDestOut" straight-alpha composition equation. + /// + public readonly struct DarkenDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenDestOut(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.DarkenDestOut(background, source, amount); - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.DarkenDestOut(background, source, amount); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Applies the "LightenDestOut" straight-alpha composition equation. + /// + public readonly struct LightenDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenDestOut(background, source, amount); - Vector512 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.LightenDestOut(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.LightenDestOut(background, source, amount); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Applies the "OverlayDestOut" straight-alpha composition equation. + /// + public readonly struct OverlayDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayDestOut(background, source, amount); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.OverlayDestOut(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.OverlayDestOut(background, source, amount); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "HardLightDestOut" straight-alpha composition equation. + /// + public readonly struct HardLightDestOut : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightDestOut(background, source, amount); - Vector256 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.HardLightDestOut(background, source, amount); - 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 = PorterDuffFunctions.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.HardLightDestOut(background, source, amount); } /// - /// A pixel blender that implements the "SubtractSrc" composition equation. + /// Applies the "NormalClear" straight-alpha composition equation. /// - public class SubtractSrc : PixelBlender + public readonly struct NormalClear : IPixelBlenderOperator { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrc Instance { get; } = new SubtractSrc(); - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalClear(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.NormalClear(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.NormalClear(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + /// Applies the "MultiplyClear" straight-alpha composition equation. + /// + public readonly struct MultiplyClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyClear(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.MultiplyClear(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.MultiplyClear(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + /// Applies the "AddClear" straight-alpha composition equation. + /// + public readonly struct AddClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddClear(background, source, amount); - 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] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); - } - } - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.AddClear(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.AddClear(background, source, amount); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Applies the "SubtractClear" straight-alpha composition equation. + /// + public readonly struct SubtractClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractClear(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.SubtractClear(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.SubtractClear(background, source, amount); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Applies the "ScreenClear" straight-alpha composition equation. + /// + public readonly struct ScreenClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenClear(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.ScreenClear(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.ScreenClear(background, source, amount); + } - 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] = PorterDuffFunctions.SubtractSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, amount); - } - } - } + /// + /// Applies the "DarkenClear" straight-alpha composition equation. + /// + public readonly struct DarkenClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenClear(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.DarkenClear(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.DarkenClear(background, source, amount); + } - Vector512 vOne = Vector512.Create(1F); + /// + /// Applies the "LightenClear" straight-alpha composition equation. + /// + public readonly struct LightenClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenClear(background, source, amount); - 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); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.LightenClear(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.LightenClear(background, source, amount); + } - destinationBase = PorterDuffFunctions.SubtractSrc(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); - } + /// + /// Applies the "OverlayClear" straight-alpha composition equation. + /// + public readonly struct OverlayClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayClear(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.OverlayClear(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.OverlayClear(background, source, amount); + } - Vector256 vOne = Vector256.Create(1F); + /// + /// Applies the "HardLightClear" straight-alpha composition equation. + /// + public readonly struct HardLightClear : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightClear(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.HardLightClear(background, source, amount); - destinationBase = PorterDuffFunctions.SubtractSrc(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); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.HardLightClear(background, source, amount); + } - 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] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + /// Applies the "NormalXor" straight-alpha composition equation. + /// + public readonly struct NormalXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalXor(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.NormalXor(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.NormalXor(background, source, amount); + } - Vector512 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 vOne = Vector512.Create(1F); + /// + /// Applies the "MultiplyXor" straight-alpha composition equation. + /// + public readonly struct MultiplyXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyXor(background, source, amount); - 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); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.MultiplyXor(background, source, amount); - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.MultiplyXor(background, source, amount); + } - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 4); - } + /// + /// Applies the "AddXor" straight-alpha composition equation. + /// + public readonly struct AddXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddXor(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.AddXor(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.AddXor(background, source, amount); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + /// Applies the "SubtractXor" straight-alpha composition equation. + /// + public readonly struct SubtractXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractXor(background, source, amount); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.SubtractXor(background, source, amount); - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.SubtractXor(background, source, amount); + } - 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] = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + /// Applies the "ScreenXor" straight-alpha composition equation. + /// + public readonly struct ScreenXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenXor(background, source, amount); /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.ScreenXor(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.ScreenXor(background, source, amount); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + /// Applies the "DarkenXor" straight-alpha composition equation. + /// + public readonly struct DarkenXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenXor(background, source, amount); - 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); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.DarkenXor(background, source, amount); - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.DarkenXor(background, source, amount); + } - Vector512 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Applies the "LightenXor" straight-alpha composition equation. + /// + public readonly struct LightenXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenXor(background, source, amount); - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.LightenXor(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.LightenXor(background, source, amount); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Applies the "OverlayXor" straight-alpha composition equation. + /// + public readonly struct OverlayXor : IPixelBlenderOperator + { + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayXor(background, source, amount); - Vector256 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.OverlayXor(background, source, amount); - 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 = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.OverlayXor(background, source, amount); + } + /// + /// Applies the "HardLightXor" straight-alpha composition equation. + /// + public readonly struct HardLightXor : IPixelBlenderOperator + { /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightXor(background, source, amount); - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.HardLightXor(background, source, amount); - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.HardLightXor(background, source, amount); + } - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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); +/// +/// Provides straight-alpha pixel blenders for each color and alpha composition pair. +/// +/// The destination pixel format. +internal static class DefaultPixelBlenders + where TPixel : unmanaged, IPixel +{ + /// + /// Blends pixels with the "NormalSrc" composition equation. + /// + public sealed class NormalSrc : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalSrc Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "MultiplySrc" composition equation. + /// + public sealed class MultiplySrc : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplySrc Instance { get; } = new(); + } - Vector512 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "AddSrc" composition equation. + /// + public sealed class AddSrc : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddSrc Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "SubtractSrc" composition equation. + /// + public sealed class SubtractSrc : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractSrc Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "ScreenSrc" composition equation. + /// + public sealed class ScreenSrc : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenSrc Instance { get; } = new(); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "DarkenSrc" composition equation. + /// + public sealed class DarkenSrc : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenSrc Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "LightenSrc" composition equation. + /// + public sealed class LightenSrc : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenSrc Instance { get; } = new(); + } - Vector256 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "OverlaySrc" composition equation. + /// + public sealed class OverlaySrc : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlaySrc Instance { get; } = new(); + } - 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 = PorterDuffFunctions.SubtractSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "HardLightSrc" composition equation. + /// + public sealed class HardLightSrc : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightSrc Instance { get; } = new(); + } - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "NormalSrcAtop" composition equation. + /// + public sealed class NormalSrcAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalSrcAtop Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "MultiplySrcAtop" composition equation. + /// + public sealed class MultiplySrcAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplySrcAtop Instance { get; } = new(); + } - Vector512 vOne = Vector512.Create(1F); + /// + /// Blends pixels with the "AddSrcAtop" composition equation. + /// + public sealed class AddSrcAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddSrcAtop Instance { get; } = new(); + } - 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); + /// + /// Blends pixels with the "SubtractSrcAtop" composition equation. + /// + public sealed class SubtractSrcAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractSrcAtop Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + /// Blends pixels with the "ScreenSrcAtop" composition equation. + /// + public sealed class ScreenSrcAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenSrcAtop Instance { get; } = new(); + } - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + /// Blends pixels with the "DarkenSrcAtop" composition equation. + /// + public sealed class DarkenSrcAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenSrcAtop Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "LightenSrcAtop" composition equation. + /// + public sealed class LightenSrcAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenSrcAtop Instance { get; } = new(); + } - Vector512 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "OverlaySrcAtop" composition equation. + /// + public sealed class OverlaySrcAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlaySrcAtop Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "HardLightSrcAtop" composition equation. + /// + public sealed class HardLightSrcAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightSrcAtop Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "NormalSrcOver" composition equation. + /// + public sealed class NormalSrcOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalSrcOver Instance { get; } = new(); + } - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "MultiplySrcOver" composition equation. + /// + public sealed class MultiplySrcOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplySrcOver Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Blends pixels with the "AddSrcOver" composition equation. + /// + public sealed class AddSrcOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddSrcOver Instance { get; } = new(); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "SubtractSrcOver" composition equation. + /// + public sealed class SubtractSrcOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractSrcOver Instance { get; } = new(); + } - Vector256 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "ScreenSrcOver" composition equation. + /// + public sealed class ScreenSrcOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenSrcOver Instance { get; } = new(); + } - 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 = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "DarkenSrcOver" composition equation. + /// + public sealed class DarkenSrcOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenSrcOver Instance { get; } = new(); + } - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "LightenSrcOver" composition equation. + /// + public sealed class LightenSrcOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenSrcOver Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "OverlaySrcOver" composition equation. + /// + public sealed class OverlaySrcOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlaySrcOver Instance { get; } = new(); + } - Vector512 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 vOne = Vector512.Create(1F); + /// + /// Blends pixels with the "HardLightSrcOver" composition equation. + /// + public sealed class HardLightSrcOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightSrcOver Instance { get; } = new(); + } - 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); + /// + /// Blends pixels with the "NormalSrcIn" composition equation. + /// + public sealed class NormalSrcIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalSrcIn Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + /// Blends pixels with the "MultiplySrcIn" composition equation. + /// + public sealed class MultiplySrcIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplySrcIn Instance { get; } = new(); + } - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + /// Blends pixels with the "AddSrcIn" composition equation. + /// + public sealed class AddSrcIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddSrcIn Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "SubtractSrcIn" composition equation. + /// + public sealed class SubtractSrcIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractSrcIn Instance { get; } = new(); + } - Vector512 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "ScreenSrcIn" composition equation. + /// + public sealed class ScreenSrcIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenSrcIn Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "DarkenSrcIn" composition equation. + /// + public sealed class DarkenSrcIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenSrcIn Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "LightenSrcIn" composition equation. + /// + public sealed class LightenSrcIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenSrcIn Instance { get; } = new(); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "OverlaySrcIn" composition equation. + /// + public sealed class OverlaySrcIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlaySrcIn Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Blends pixels with the "HardLightSrcIn" composition equation. + /// + public sealed class HardLightSrcIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightSrcIn Instance { get; } = new(); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "NormalSrcOut" composition equation. + /// + public sealed class NormalSrcOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalSrcOut Instance { get; } = new(); + } - Vector256 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "MultiplySrcOut" composition equation. + /// + public sealed class MultiplySrcOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplySrcOut Instance { get; } = new(); + } - 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 = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "AddSrcOut" composition equation. + /// + public sealed class AddSrcOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddSrcOut Instance { get; } = new(); } /// - /// A pixel blender that implements the "ScreenSrc" composition equation. + /// Blends pixels with the "SubtractSrcOut" composition equation. /// - public class ScreenSrc : PixelBlender + public sealed class SubtractSrcOut : + DefaultPixelBlender { /// - /// Gets the static instance of this blender. + /// Gets the shared blender instance. /// - public static ScreenSrc Instance { get; } = new ScreenSrc(); + public static SubtractSrcOut Instance { get; } = new(); + } - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } + /// + /// Blends pixels with the "ScreenSrcOut" composition equation. + /// + public sealed class ScreenSrcOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenSrcOut Instance { get; } = new(); + } - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); + /// + /// Blends pixels with the "DarkenSrcOut" composition equation. + /// + public sealed class DarkenSrcOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenSrcOut Instance { get; } = new(); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "LightenSrcOut" composition equation. + /// + public sealed class LightenSrcOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenSrcOut Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); + /// + /// Blends pixels with the "OverlaySrcOut" composition equation. + /// + public sealed class OverlaySrcOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlaySrcOut Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } + /// + /// Blends pixels with the "HardLightSrcOut" composition equation. + /// + public sealed class HardLightSrcOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightSrcOut Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "NormalDest" composition equation. + /// + public sealed class NormalDest : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalDest Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + /// + /// Blends pixels with the "MultiplyDest" composition equation. + /// + public sealed class MultiplyDest : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyDest Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrc(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] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrc(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] = PorterDuffFunctions.ScreenSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrc(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] = PorterDuffFunctions.ScreenSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrc(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] = PorterDuffFunctions.ScreenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrc(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] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrc(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] = PorterDuffFunctions.ScreenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrc(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] = PorterDuffFunctions.ScreenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrc" composition equation. - /// - public class DarkenSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrc Instance { get; } = new DarkenSrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrc(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] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrc(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] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrc(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] = PorterDuffFunctions.DarkenSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrc(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] = PorterDuffFunctions.DarkenSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrc(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] = PorterDuffFunctions.DarkenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrc(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] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrc(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] = PorterDuffFunctions.DarkenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrc(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] = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrc" composition equation. - /// - public class LightenSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrc Instance { get; } = new LightenSrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrc(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] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrc(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] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrc(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] = PorterDuffFunctions.LightenSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrc(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] = PorterDuffFunctions.LightenSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrc(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] = PorterDuffFunctions.LightenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrc(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] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrc(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] = PorterDuffFunctions.LightenSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrc(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] = PorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrc" composition equation. - /// - public class OverlaySrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrc Instance { get; } = new OverlaySrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlaySrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrc(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] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrc(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] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrc(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] = PorterDuffFunctions.OverlaySrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrc(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] = PorterDuffFunctions.OverlaySrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrc(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] = PorterDuffFunctions.OverlaySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrc(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] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrc(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] = PorterDuffFunctions.OverlaySrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrc(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] = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrc" composition equation. - /// - public class HardLightSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrc Instance { get; } = new HardLightSrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrc(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] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrc(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] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrc(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] = PorterDuffFunctions.HardLightSrc(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrc(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] = PorterDuffFunctions.HardLightSrc(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrc(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] = PorterDuffFunctions.HardLightSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrc(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] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrc(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] = PorterDuffFunctions.HardLightSrc(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrc(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] = PorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalSrcAtop" composition equation. - /// - public class NormalSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcAtop Instance { get; } = new NormalSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcAtop(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] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcAtop(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] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcAtop(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] = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcAtop(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] = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcAtop(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] = PorterDuffFunctions.NormalSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcAtop(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] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcAtop(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] = PorterDuffFunctions.NormalSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcAtop(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] = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrcAtop" composition equation. - /// - public class MultiplySrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcAtop Instance { get; } = new MultiplySrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplySrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcAtop(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] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcAtop(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] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcAtop(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] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcAtop(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] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcAtop(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] = PorterDuffFunctions.MultiplySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcAtop(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] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcAtop(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] = PorterDuffFunctions.MultiplySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcAtop(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] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrcAtop" composition equation. - /// - public class AddSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcAtop Instance { get; } = new AddSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcAtop(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] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcAtop(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] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcAtop(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] = PorterDuffFunctions.AddSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcAtop(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] = PorterDuffFunctions.AddSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcAtop(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] = PorterDuffFunctions.AddSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcAtop(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] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcAtop(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] = PorterDuffFunctions.AddSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcAtop(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] = PorterDuffFunctions.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrcAtop" composition equation. - /// - public class SubtractSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcAtop Instance { get; } = new SubtractSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcAtop(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] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcAtop(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] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcAtop(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] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcAtop(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] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcAtop(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] = PorterDuffFunctions.SubtractSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcAtop(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] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcAtop(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] = PorterDuffFunctions.SubtractSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcAtop(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] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrcAtop" composition equation. - /// - public class ScreenSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcAtop Instance { get; } = new ScreenSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcAtop(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] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcAtop(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] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcAtop(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] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcAtop(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] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcAtop(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] = PorterDuffFunctions.ScreenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcAtop(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] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcAtop(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] = PorterDuffFunctions.ScreenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcAtop(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] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrcAtop" composition equation. - /// - public class DarkenSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcAtop Instance { get; } = new DarkenSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcAtop(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] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcAtop(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] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcAtop(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] = PorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcAtop(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] = PorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcAtop(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] = PorterDuffFunctions.DarkenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcAtop(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] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcAtop(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] = PorterDuffFunctions.DarkenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcAtop(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] = PorterDuffFunctions.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrcAtop" composition equation. - /// - public class LightenSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcAtop Instance { get; } = new LightenSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcAtop(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] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcAtop(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] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcAtop(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] = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcAtop(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] = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcAtop(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] = PorterDuffFunctions.LightenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcAtop(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] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcAtop(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] = PorterDuffFunctions.LightenSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcAtop(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] = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrcAtop" composition equation. - /// - public class OverlaySrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcAtop Instance { get; } = new OverlaySrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlaySrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcAtop(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] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcAtop(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] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcAtop(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] = PorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcAtop(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] = PorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcAtop(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] = PorterDuffFunctions.OverlaySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcAtop(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] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcAtop(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] = PorterDuffFunctions.OverlaySrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcAtop(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] = PorterDuffFunctions.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrcAtop" composition equation. - /// - public class HardLightSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcAtop Instance { get; } = new HardLightSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcAtop(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] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcAtop(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] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcAtop(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] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcAtop(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] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcAtop(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] = PorterDuffFunctions.HardLightSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcAtop(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] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcAtop(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] = PorterDuffFunctions.HardLightSrcAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcAtop(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] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalSrcOver" composition equation. - /// - public class NormalSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcOver Instance { get; } = new NormalSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcOver(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] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcOver(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] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcOver(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] = PorterDuffFunctions.NormalSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcOver(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] = PorterDuffFunctions.NormalSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcOver(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] = PorterDuffFunctions.NormalSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcOver(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] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcOver(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] = PorterDuffFunctions.NormalSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcOver(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] = PorterDuffFunctions.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrcOver" composition equation. - /// - public class MultiplySrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcOver Instance { get; } = new MultiplySrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplySrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcOver(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] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcOver(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] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcOver(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] = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcOver(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] = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcOver(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] = PorterDuffFunctions.MultiplySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcOver(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] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcOver(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] = PorterDuffFunctions.MultiplySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcOver(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] = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrcOver" composition equation. - /// - public class AddSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcOver Instance { get; } = new AddSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcOver(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] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcOver(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] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcOver(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] = PorterDuffFunctions.AddSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcOver(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] = PorterDuffFunctions.AddSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcOver(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] = PorterDuffFunctions.AddSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcOver(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] = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcOver(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] = PorterDuffFunctions.AddSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcOver(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] = PorterDuffFunctions.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrcOver" composition equation. - /// - public class SubtractSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcOver Instance { get; } = new SubtractSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcOver(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] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcOver(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] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcOver(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] = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcOver(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] = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcOver(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] = PorterDuffFunctions.SubtractSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcOver(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] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcOver(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] = PorterDuffFunctions.SubtractSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcOver(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] = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrcOver" composition equation. - /// - public class ScreenSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcOver Instance { get; } = new ScreenSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcOver(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] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcOver(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] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcOver(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] = PorterDuffFunctions.ScreenSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcOver(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] = PorterDuffFunctions.ScreenSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcOver(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] = PorterDuffFunctions.ScreenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcOver(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] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcOver(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] = PorterDuffFunctions.ScreenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcOver(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] = PorterDuffFunctions.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrcOver" composition equation. - /// - public class DarkenSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcOver Instance { get; } = new DarkenSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcOver(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] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcOver(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] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcOver(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] = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcOver(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] = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcOver(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] = PorterDuffFunctions.DarkenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcOver(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] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcOver(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] = PorterDuffFunctions.DarkenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcOver(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] = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrcOver" composition equation. - /// - public class LightenSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcOver Instance { get; } = new LightenSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcOver(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] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcOver(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] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcOver(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] = PorterDuffFunctions.LightenSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcOver(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] = PorterDuffFunctions.LightenSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcOver(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] = PorterDuffFunctions.LightenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcOver(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] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcOver(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] = PorterDuffFunctions.LightenSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcOver(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] = PorterDuffFunctions.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrcOver" composition equation. - /// - public class OverlaySrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcOver Instance { get; } = new OverlaySrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlaySrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcOver(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] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcOver(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] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcOver(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] = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcOver(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] = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcOver(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] = PorterDuffFunctions.OverlaySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcOver(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] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcOver(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] = PorterDuffFunctions.OverlaySrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcOver(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] = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrcOver" composition equation. - /// - public class HardLightSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcOver Instance { get; } = new HardLightSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcOver(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] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcOver(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] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcOver(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] = PorterDuffFunctions.HardLightSrcOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcOver(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] = PorterDuffFunctions.HardLightSrcOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcOver(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] = PorterDuffFunctions.HardLightSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcOver(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] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcOver(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] = PorterDuffFunctions.HardLightSrcOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcOver(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] = PorterDuffFunctions.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalSrcIn" composition equation. - /// - public class NormalSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcIn Instance { get; } = new NormalSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcIn(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] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcIn(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] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcIn(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] = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcIn(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] = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcIn(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] = PorterDuffFunctions.NormalSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcIn(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] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcIn(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] = PorterDuffFunctions.NormalSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcIn(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] = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrcIn" composition equation. - /// - public class MultiplySrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcIn Instance { get; } = new MultiplySrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplySrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcIn(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] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcIn(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] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcIn(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] = PorterDuffFunctions.MultiplySrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcIn(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] = PorterDuffFunctions.MultiplySrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcIn(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] = PorterDuffFunctions.MultiplySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcIn(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] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcIn(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] = PorterDuffFunctions.MultiplySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcIn(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] = PorterDuffFunctions.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrcIn" composition equation. - /// - public class AddSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcIn Instance { get; } = new AddSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcIn(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] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcIn(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] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcIn(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] = PorterDuffFunctions.AddSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcIn(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] = PorterDuffFunctions.AddSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcIn(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] = PorterDuffFunctions.AddSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcIn(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] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcIn(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] = PorterDuffFunctions.AddSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcIn(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] = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrcIn" composition equation. - /// - public class SubtractSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcIn Instance { get; } = new SubtractSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcIn(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] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcIn(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] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcIn(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] = PorterDuffFunctions.SubtractSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcIn(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] = PorterDuffFunctions.SubtractSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcIn(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] = PorterDuffFunctions.SubtractSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcIn(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] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcIn(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] = PorterDuffFunctions.SubtractSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcIn(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] = PorterDuffFunctions.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrcIn" composition equation. - /// - public class ScreenSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcIn Instance { get; } = new ScreenSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcIn(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] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcIn(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] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcIn(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] = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcIn(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] = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcIn(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] = PorterDuffFunctions.ScreenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcIn(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] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcIn(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] = PorterDuffFunctions.ScreenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcIn(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] = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrcIn" composition equation. - /// - public class DarkenSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcIn Instance { get; } = new DarkenSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcIn(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] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcIn(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] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcIn(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] = PorterDuffFunctions.DarkenSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcIn(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] = PorterDuffFunctions.DarkenSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcIn(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] = PorterDuffFunctions.DarkenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcIn(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] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcIn(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] = PorterDuffFunctions.DarkenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcIn(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] = PorterDuffFunctions.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrcIn" composition equation. - /// - public class LightenSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcIn Instance { get; } = new LightenSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcIn(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] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcIn(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] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcIn(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] = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcIn(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] = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcIn(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] = PorterDuffFunctions.LightenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcIn(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] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcIn(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] = PorterDuffFunctions.LightenSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcIn(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] = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrcIn" composition equation. - /// - public class OverlaySrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcIn Instance { get; } = new OverlaySrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlaySrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcIn(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] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcIn(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] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcIn(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] = PorterDuffFunctions.OverlaySrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcIn(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] = PorterDuffFunctions.OverlaySrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcIn(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] = PorterDuffFunctions.OverlaySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcIn(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] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcIn(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] = PorterDuffFunctions.OverlaySrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcIn(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] = PorterDuffFunctions.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrcIn" composition equation. - /// - public class HardLightSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcIn Instance { get; } = new HardLightSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcIn(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] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcIn(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] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcIn(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] = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcIn(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] = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcIn(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] = PorterDuffFunctions.HardLightSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcIn(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] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcIn(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] = PorterDuffFunctions.HardLightSrcIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcIn(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] = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalSrcOut" composition equation. - /// - public class NormalSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcOut Instance { get; } = new NormalSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcOut(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] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcOut(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] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcOut(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] = PorterDuffFunctions.NormalSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcOut(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] = PorterDuffFunctions.NormalSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcOut(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] = PorterDuffFunctions.NormalSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcOut(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] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcOut(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] = PorterDuffFunctions.NormalSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcOut(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] = PorterDuffFunctions.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrcOut" composition equation. - /// - public class MultiplySrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcOut Instance { get; } = new MultiplySrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplySrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcOut(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] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcOut(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] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcOut(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] = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcOut(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] = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcOut(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] = PorterDuffFunctions.MultiplySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcOut(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] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcOut(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] = PorterDuffFunctions.MultiplySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcOut(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] = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrcOut" composition equation. - /// - public class AddSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcOut Instance { get; } = new AddSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcOut(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] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcOut(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] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcOut(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] = PorterDuffFunctions.AddSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcOut(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] = PorterDuffFunctions.AddSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcOut(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] = PorterDuffFunctions.AddSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcOut(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] = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcOut(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] = PorterDuffFunctions.AddSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcOut(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] = PorterDuffFunctions.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrcOut" composition equation. - /// - public class SubtractSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcOut Instance { get; } = new SubtractSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcOut(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] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcOut(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] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcOut(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] = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcOut(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] = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcOut(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] = PorterDuffFunctions.SubtractSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcOut(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] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcOut(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] = PorterDuffFunctions.SubtractSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcOut(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] = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrcOut" composition equation. - /// - public class ScreenSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcOut Instance { get; } = new ScreenSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcOut(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] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcOut(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] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcOut(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] = PorterDuffFunctions.ScreenSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcOut(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] = PorterDuffFunctions.ScreenSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcOut(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] = PorterDuffFunctions.ScreenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcOut(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] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcOut(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] = PorterDuffFunctions.ScreenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcOut(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] = PorterDuffFunctions.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrcOut" composition equation. - /// - public class DarkenSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcOut Instance { get; } = new DarkenSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcOut(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] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcOut(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] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcOut(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] = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcOut(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] = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcOut(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] = PorterDuffFunctions.DarkenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcOut(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] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcOut(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] = PorterDuffFunctions.DarkenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcOut(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] = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrcOut" composition equation. - /// - public class LightenSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcOut Instance { get; } = new LightenSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcOut(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] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcOut(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] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcOut(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] = PorterDuffFunctions.LightenSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcOut(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] = PorterDuffFunctions.LightenSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcOut(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] = PorterDuffFunctions.LightenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcOut(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] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcOut(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] = PorterDuffFunctions.LightenSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcOut(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] = PorterDuffFunctions.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrcOut" composition equation. - /// - public class OverlaySrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcOut Instance { get; } = new OverlaySrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlaySrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcOut(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] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcOut(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] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcOut(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] = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcOut(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] = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcOut(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] = PorterDuffFunctions.OverlaySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcOut(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] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcOut(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] = PorterDuffFunctions.OverlaySrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcOut(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] = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrcOut" composition equation. - /// - public class HardLightSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcOut Instance { get; } = new HardLightSrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcOut(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] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcOut(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] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcOut(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] = PorterDuffFunctions.HardLightSrcOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcOut(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] = PorterDuffFunctions.HardLightSrcOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcOut(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] = PorterDuffFunctions.HardLightSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcOut(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] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcOut(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] = PorterDuffFunctions.HardLightSrcOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcOut(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] = PorterDuffFunctions.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDest" composition equation. - /// - public class NormalDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDest Instance { get; } = new NormalDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDest(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] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDest(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] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDest(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] = PorterDuffFunctions.NormalDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDest(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] = PorterDuffFunctions.NormalDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDest(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] = PorterDuffFunctions.NormalDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDest(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] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDest(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] = PorterDuffFunctions.NormalDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDest(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] = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDest" composition equation. - /// - public class MultiplyDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDest Instance { get; } = new MultiplyDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDest(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] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDest(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] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDest(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] = PorterDuffFunctions.MultiplyDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDest(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] = PorterDuffFunctions.MultiplyDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDest(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] = PorterDuffFunctions.MultiplyDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDest(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] = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDest(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] = PorterDuffFunctions.MultiplyDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDest(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] = PorterDuffFunctions.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDest" composition equation. - /// - public class AddDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDest Instance { get; } = new AddDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDest(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] = PorterDuffFunctions.AddDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDest(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] = PorterDuffFunctions.AddDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDest(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] = PorterDuffFunctions.AddDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDest(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] = PorterDuffFunctions.AddDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDest(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] = PorterDuffFunctions.AddDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDest(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] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDest(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] = PorterDuffFunctions.AddDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDest(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] = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDest" composition equation. - /// - public class SubtractDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDest Instance { get; } = new SubtractDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDest(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] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDest(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] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDest(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] = PorterDuffFunctions.SubtractDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDest(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] = PorterDuffFunctions.SubtractDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDest(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] = PorterDuffFunctions.SubtractDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDest(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] = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDest(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] = PorterDuffFunctions.SubtractDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDest(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] = PorterDuffFunctions.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDest" composition equation. - /// - public class ScreenDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDest Instance { get; } = new ScreenDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDest(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] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDest(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] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDest(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] = PorterDuffFunctions.ScreenDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDest(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] = PorterDuffFunctions.ScreenDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDest(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] = PorterDuffFunctions.ScreenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDest(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] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDest(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] = PorterDuffFunctions.ScreenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDest(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] = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDest" composition equation. - /// - public class DarkenDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDest Instance { get; } = new DarkenDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDest(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] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDest(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] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDest(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] = PorterDuffFunctions.DarkenDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDest(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] = PorterDuffFunctions.DarkenDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDest(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] = PorterDuffFunctions.DarkenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDest(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] = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDest(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] = PorterDuffFunctions.DarkenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDest(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] = PorterDuffFunctions.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDest" composition equation. - /// - public class LightenDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDest Instance { get; } = new LightenDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDest(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] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDest(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] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDest(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] = PorterDuffFunctions.LightenDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDest(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] = PorterDuffFunctions.LightenDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDest(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] = PorterDuffFunctions.LightenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDest(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] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDest(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] = PorterDuffFunctions.LightenDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDest(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] = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDest" composition equation. - /// - public class OverlayDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDest Instance { get; } = new OverlayDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDest(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] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDest(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] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDest(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] = PorterDuffFunctions.OverlayDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDest(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] = PorterDuffFunctions.OverlayDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDest(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] = PorterDuffFunctions.OverlayDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDest(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] = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDest(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] = PorterDuffFunctions.OverlayDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDest(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] = PorterDuffFunctions.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDest" composition equation. - /// - public class HardLightDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDest Instance { get; } = new HardLightDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDest(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] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDest(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] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDest(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] = PorterDuffFunctions.HardLightDest(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDest(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] = PorterDuffFunctions.HardLightDest(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDest(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] = PorterDuffFunctions.HardLightDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDest(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] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDest(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] = PorterDuffFunctions.HardLightDest(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDest(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] = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDestAtop" composition equation. - /// - public class NormalDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestAtop Instance { get; } = new NormalDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestAtop(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] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestAtop(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] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestAtop(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] = PorterDuffFunctions.NormalDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestAtop(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] = PorterDuffFunctions.NormalDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestAtop(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] = PorterDuffFunctions.NormalDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestAtop(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] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestAtop(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] = PorterDuffFunctions.NormalDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestAtop(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] = PorterDuffFunctions.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDestAtop" composition equation. - /// - public class MultiplyDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestAtop Instance { get; } = new MultiplyDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestAtop(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] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestAtop(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] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestAtop(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] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestAtop(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] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestAtop(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] = PorterDuffFunctions.MultiplyDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestAtop(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] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestAtop(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] = PorterDuffFunctions.MultiplyDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestAtop(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] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestAtop" composition equation. - /// - public class AddDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestAtop Instance { get; } = new AddDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestAtop(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] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestAtop(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] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestAtop(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] = PorterDuffFunctions.AddDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestAtop(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] = PorterDuffFunctions.AddDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestAtop(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] = PorterDuffFunctions.AddDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestAtop(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] = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestAtop(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] = PorterDuffFunctions.AddDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestAtop(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] = PorterDuffFunctions.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDestAtop" composition equation. - /// - public class SubtractDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestAtop Instance { get; } = new SubtractDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestAtop(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] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestAtop(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] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestAtop(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] = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestAtop(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] = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestAtop(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] = PorterDuffFunctions.SubtractDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestAtop(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] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestAtop(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] = PorterDuffFunctions.SubtractDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestAtop(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] = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDestAtop" composition equation. - /// - public class ScreenDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestAtop Instance { get; } = new ScreenDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestAtop(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] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestAtop(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] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestAtop(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] = PorterDuffFunctions.ScreenDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestAtop(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] = PorterDuffFunctions.ScreenDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestAtop(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] = PorterDuffFunctions.ScreenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestAtop(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] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestAtop(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] = PorterDuffFunctions.ScreenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestAtop(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] = PorterDuffFunctions.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestAtop" composition equation. - /// - public class DarkenDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestAtop Instance { get; } = new DarkenDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestAtop(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] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestAtop(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] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestAtop(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] = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestAtop(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] = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestAtop(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] = PorterDuffFunctions.DarkenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestAtop(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] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestAtop(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] = PorterDuffFunctions.DarkenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestAtop(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] = PorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDestAtop" composition equation. - /// - public class LightenDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestAtop Instance { get; } = new LightenDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestAtop(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] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestAtop(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] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestAtop(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] = PorterDuffFunctions.LightenDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestAtop(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] = PorterDuffFunctions.LightenDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestAtop(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] = PorterDuffFunctions.LightenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestAtop(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] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestAtop(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] = PorterDuffFunctions.LightenDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestAtop(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] = PorterDuffFunctions.LightenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDestAtop" composition equation. - /// - public class OverlayDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestAtop Instance { get; } = new OverlayDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestAtop(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] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestAtop(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] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestAtop(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] = PorterDuffFunctions.OverlayDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestAtop(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] = PorterDuffFunctions.OverlayDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestAtop(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] = PorterDuffFunctions.OverlayDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestAtop(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] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestAtop(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] = PorterDuffFunctions.OverlayDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestAtop(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] = PorterDuffFunctions.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestAtop" composition equation. - /// - public class HardLightDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestAtop Instance { get; } = new HardLightDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestAtop(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] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestAtop(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] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestAtop(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] = PorterDuffFunctions.HardLightDestAtop(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestAtop(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] = PorterDuffFunctions.HardLightDestAtop(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestAtop(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] = PorterDuffFunctions.HardLightDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestAtop(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] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestAtop(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] = PorterDuffFunctions.HardLightDestAtop(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestAtop(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] = PorterDuffFunctions.HardLightDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestAtop(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDestOver" composition equation. - /// - public class NormalDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestOver Instance { get; } = new NormalDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestOver(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] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestOver(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] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestOver(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] = PorterDuffFunctions.NormalDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestOver(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] = PorterDuffFunctions.NormalDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestOver(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] = PorterDuffFunctions.NormalDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestOver(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] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestOver(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] = PorterDuffFunctions.NormalDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestOver(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] = PorterDuffFunctions.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDestOver" composition equation. - /// - public class MultiplyDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestOver Instance { get; } = new MultiplyDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestOver(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] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestOver(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] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestOver(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] = PorterDuffFunctions.MultiplyDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestOver(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] = PorterDuffFunctions.MultiplyDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestOver(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] = PorterDuffFunctions.MultiplyDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestOver(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] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestOver(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] = PorterDuffFunctions.MultiplyDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestOver(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] = PorterDuffFunctions.MultiplyDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestOver" composition equation. - /// - public class AddDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestOver Instance { get; } = new AddDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestOver(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] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestOver(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] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestOver(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] = PorterDuffFunctions.AddDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestOver(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] = PorterDuffFunctions.AddDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestOver(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] = PorterDuffFunctions.AddDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestOver(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] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestOver(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] = PorterDuffFunctions.AddDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestOver(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] = PorterDuffFunctions.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDestOver" composition equation. - /// - public class SubtractDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestOver Instance { get; } = new SubtractDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestOver(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] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestOver(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] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestOver(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] = PorterDuffFunctions.SubtractDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestOver(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] = PorterDuffFunctions.SubtractDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestOver(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] = PorterDuffFunctions.SubtractDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestOver(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] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestOver(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] = PorterDuffFunctions.SubtractDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestOver(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] = PorterDuffFunctions.SubtractDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDestOver" composition equation. - /// - public class ScreenDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestOver Instance { get; } = new ScreenDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestOver(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] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestOver(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] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestOver(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] = PorterDuffFunctions.ScreenDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestOver(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] = PorterDuffFunctions.ScreenDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestOver(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] = PorterDuffFunctions.ScreenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestOver(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] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestOver(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] = PorterDuffFunctions.ScreenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestOver(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] = PorterDuffFunctions.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestOver" composition equation. - /// - public class DarkenDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestOver Instance { get; } = new DarkenDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestOver(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] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestOver(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] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestOver(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] = PorterDuffFunctions.DarkenDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestOver(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] = PorterDuffFunctions.DarkenDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestOver(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] = PorterDuffFunctions.DarkenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestOver(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] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestOver(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] = PorterDuffFunctions.DarkenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestOver(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] = PorterDuffFunctions.DarkenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDestOver" composition equation. - /// - public class LightenDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestOver Instance { get; } = new LightenDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestOver(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] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestOver(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] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestOver(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] = PorterDuffFunctions.LightenDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestOver(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] = PorterDuffFunctions.LightenDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestOver(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] = PorterDuffFunctions.LightenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestOver(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] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestOver(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] = PorterDuffFunctions.LightenDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestOver(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] = PorterDuffFunctions.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDestOver" composition equation. - /// - public class OverlayDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestOver Instance { get; } = new OverlayDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestOver(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] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestOver(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] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestOver(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] = PorterDuffFunctions.OverlayDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestOver(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] = PorterDuffFunctions.OverlayDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestOver(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] = PorterDuffFunctions.OverlayDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestOver(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] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestOver(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] = PorterDuffFunctions.OverlayDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestOver(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] = PorterDuffFunctions.OverlayDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestOver" composition equation. - /// - public class HardLightDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestOver Instance { get; } = new HardLightDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestOver(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] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestOver(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] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestOver(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] = PorterDuffFunctions.HardLightDestOver(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestOver(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] = PorterDuffFunctions.HardLightDestOver(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestOver(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] = PorterDuffFunctions.HardLightDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestOver(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] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestOver(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] = PorterDuffFunctions.HardLightDestOver(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestOver(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] = PorterDuffFunctions.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestOver(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDestIn" composition equation. - /// - public class NormalDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestIn Instance { get; } = new NormalDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestIn(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] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestIn(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] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestIn(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] = PorterDuffFunctions.NormalDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestIn(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] = PorterDuffFunctions.NormalDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestIn(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] = PorterDuffFunctions.NormalDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestIn(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] = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestIn(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] = PorterDuffFunctions.NormalDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestIn(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] = PorterDuffFunctions.NormalDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDestIn" composition equation. - /// - public class MultiplyDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestIn Instance { get; } = new MultiplyDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestIn(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] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestIn(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] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestIn(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] = PorterDuffFunctions.MultiplyDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestIn(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] = PorterDuffFunctions.MultiplyDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestIn(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] = PorterDuffFunctions.MultiplyDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestIn(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] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestIn(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] = PorterDuffFunctions.MultiplyDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestIn(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] = PorterDuffFunctions.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestIn" composition equation. - /// - public class AddDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestIn Instance { get; } = new AddDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestIn(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] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestIn(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] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestIn(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] = PorterDuffFunctions.AddDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestIn(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] = PorterDuffFunctions.AddDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestIn(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] = PorterDuffFunctions.AddDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestIn(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] = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestIn(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] = PorterDuffFunctions.AddDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestIn(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] = PorterDuffFunctions.AddDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDestIn" composition equation. - /// - public class SubtractDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestIn Instance { get; } = new SubtractDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestIn(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] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestIn(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] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestIn(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] = PorterDuffFunctions.SubtractDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestIn(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] = PorterDuffFunctions.SubtractDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestIn(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] = PorterDuffFunctions.SubtractDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestIn(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] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestIn(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] = PorterDuffFunctions.SubtractDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestIn(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] = PorterDuffFunctions.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDestIn" composition equation. - /// - public class ScreenDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestIn Instance { get; } = new ScreenDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestIn(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] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestIn(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] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestIn(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] = PorterDuffFunctions.ScreenDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestIn(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] = PorterDuffFunctions.ScreenDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestIn(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] = PorterDuffFunctions.ScreenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestIn(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] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestIn(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] = PorterDuffFunctions.ScreenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestIn(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] = PorterDuffFunctions.ScreenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestIn" composition equation. - /// - public class DarkenDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestIn Instance { get; } = new DarkenDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestIn(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] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestIn(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] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestIn(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] = PorterDuffFunctions.DarkenDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestIn(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] = PorterDuffFunctions.DarkenDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestIn(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] = PorterDuffFunctions.DarkenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestIn(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] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestIn(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] = PorterDuffFunctions.DarkenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestIn(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] = PorterDuffFunctions.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDestIn" composition equation. - /// - public class LightenDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestIn Instance { get; } = new LightenDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestIn(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] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestIn(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] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestIn(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] = PorterDuffFunctions.LightenDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestIn(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] = PorterDuffFunctions.LightenDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestIn(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] = PorterDuffFunctions.LightenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestIn(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] = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestIn(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] = PorterDuffFunctions.LightenDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestIn(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] = PorterDuffFunctions.LightenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDestIn" composition equation. - /// - public class OverlayDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestIn Instance { get; } = new OverlayDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestIn(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] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestIn(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] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestIn(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] = PorterDuffFunctions.OverlayDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestIn(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] = PorterDuffFunctions.OverlayDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestIn(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] = PorterDuffFunctions.OverlayDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestIn(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] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestIn(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] = PorterDuffFunctions.OverlayDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestIn(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] = PorterDuffFunctions.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestIn" composition equation. - /// - public class HardLightDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestIn Instance { get; } = new HardLightDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestIn(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] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestIn(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] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestIn(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] = PorterDuffFunctions.HardLightDestIn(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestIn(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] = PorterDuffFunctions.HardLightDestIn(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestIn(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] = PorterDuffFunctions.HardLightDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestIn(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] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestIn(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] = PorterDuffFunctions.HardLightDestIn(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestIn(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] = PorterDuffFunctions.HardLightDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestIn(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDestOut" composition equation. - /// - public class NormalDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestOut Instance { get; } = new NormalDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestOut(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] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestOut(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] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestOut(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] = PorterDuffFunctions.NormalDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestOut(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] = PorterDuffFunctions.NormalDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestOut(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] = PorterDuffFunctions.NormalDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestOut(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] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestOut(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] = PorterDuffFunctions.NormalDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestOut(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] = PorterDuffFunctions.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDestOut" composition equation. - /// - public class MultiplyDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestOut Instance { get; } = new MultiplyDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestOut(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] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestOut(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] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestOut(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] = PorterDuffFunctions.MultiplyDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestOut(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] = PorterDuffFunctions.MultiplyDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestOut(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] = PorterDuffFunctions.MultiplyDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestOut(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] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestOut(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] = PorterDuffFunctions.MultiplyDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestOut(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] = PorterDuffFunctions.MultiplyDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestOut" composition equation. - /// - public class AddDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestOut Instance { get; } = new AddDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestOut(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] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestOut(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] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestOut(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] = PorterDuffFunctions.AddDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestOut(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] = PorterDuffFunctions.AddDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestOut(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] = PorterDuffFunctions.AddDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestOut(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] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestOut(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] = PorterDuffFunctions.AddDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestOut(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] = PorterDuffFunctions.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDestOut" composition equation. - /// - public class SubtractDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestOut Instance { get; } = new SubtractDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestOut(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] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestOut(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] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestOut(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] = PorterDuffFunctions.SubtractDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestOut(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] = PorterDuffFunctions.SubtractDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestOut(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] = PorterDuffFunctions.SubtractDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestOut(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] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestOut(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] = PorterDuffFunctions.SubtractDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestOut(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] = PorterDuffFunctions.SubtractDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDestOut" composition equation. - /// - public class ScreenDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestOut Instance { get; } = new ScreenDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestOut(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] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestOut(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] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestOut(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] = PorterDuffFunctions.ScreenDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestOut(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] = PorterDuffFunctions.ScreenDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestOut(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] = PorterDuffFunctions.ScreenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestOut(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] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestOut(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] = PorterDuffFunctions.ScreenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestOut(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] = PorterDuffFunctions.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestOut" composition equation. - /// - public class DarkenDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestOut Instance { get; } = new DarkenDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestOut(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] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestOut(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] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestOut(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] = PorterDuffFunctions.DarkenDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestOut(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] = PorterDuffFunctions.DarkenDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestOut(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] = PorterDuffFunctions.DarkenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestOut(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] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestOut(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] = PorterDuffFunctions.DarkenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestOut(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] = PorterDuffFunctions.DarkenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDestOut" composition equation. - /// - public class LightenDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestOut Instance { get; } = new LightenDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestOut(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] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestOut(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] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestOut(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] = PorterDuffFunctions.LightenDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestOut(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] = PorterDuffFunctions.LightenDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestOut(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] = PorterDuffFunctions.LightenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestOut(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] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestOut(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] = PorterDuffFunctions.LightenDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestOut(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] = PorterDuffFunctions.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDestOut" composition equation. - /// - public class OverlayDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestOut Instance { get; } = new OverlayDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestOut(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] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestOut(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] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestOut(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] = PorterDuffFunctions.OverlayDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestOut(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] = PorterDuffFunctions.OverlayDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestOut(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] = PorterDuffFunctions.OverlayDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestOut(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] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestOut(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] = PorterDuffFunctions.OverlayDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestOut(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] = PorterDuffFunctions.OverlayDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestOut" composition equation. - /// - public class HardLightDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestOut Instance { get; } = new HardLightDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestOut(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] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestOut(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] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestOut(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] = PorterDuffFunctions.HardLightDestOut(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestOut(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] = PorterDuffFunctions.HardLightDestOut(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestOut(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] = PorterDuffFunctions.HardLightDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestOut(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] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestOut(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] = PorterDuffFunctions.HardLightDestOut(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestOut(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] = PorterDuffFunctions.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestOut(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalClear" composition equation. - /// - public class NormalClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalClear Instance { get; } = new NormalClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalClear(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] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalClear(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] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalClear(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] = PorterDuffFunctions.NormalClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalClear(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] = PorterDuffFunctions.NormalClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalClear(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] = PorterDuffFunctions.NormalClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalClear(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] = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalClear(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] = PorterDuffFunctions.NormalClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalClear(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] = PorterDuffFunctions.NormalClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyClear" composition equation. - /// - public class MultiplyClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyClear Instance { get; } = new MultiplyClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyClear(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] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyClear(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] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyClear(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] = PorterDuffFunctions.MultiplyClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyClear(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] = PorterDuffFunctions.MultiplyClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyClear(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] = PorterDuffFunctions.MultiplyClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyClear(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] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyClear(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] = PorterDuffFunctions.MultiplyClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyClear(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] = PorterDuffFunctions.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddClear" composition equation. - /// - public class AddClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddClear Instance { get; } = new AddClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddClear(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] = PorterDuffFunctions.AddClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddClear(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] = PorterDuffFunctions.AddClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddClear(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] = PorterDuffFunctions.AddClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddClear(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] = PorterDuffFunctions.AddClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddClear(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] = PorterDuffFunctions.AddClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddClear(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] = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddClear(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] = PorterDuffFunctions.AddClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddClear(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] = PorterDuffFunctions.AddClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractClear" composition equation. - /// - public class SubtractClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractClear Instance { get; } = new SubtractClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractClear(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] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractClear(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] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractClear(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] = PorterDuffFunctions.SubtractClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractClear(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] = PorterDuffFunctions.SubtractClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractClear(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] = PorterDuffFunctions.SubtractClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractClear(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] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractClear(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] = PorterDuffFunctions.SubtractClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractClear(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] = PorterDuffFunctions.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenClear" composition equation. - /// - public class ScreenClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenClear Instance { get; } = new ScreenClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenClear(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] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenClear(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] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenClear(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] = PorterDuffFunctions.ScreenClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenClear(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] = PorterDuffFunctions.ScreenClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenClear(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] = PorterDuffFunctions.ScreenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenClear(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] = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenClear(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] = PorterDuffFunctions.ScreenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenClear(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] = PorterDuffFunctions.ScreenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenClear" composition equation. - /// - public class DarkenClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenClear Instance { get; } = new DarkenClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenClear(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] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenClear(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] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenClear(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] = PorterDuffFunctions.DarkenClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenClear(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] = PorterDuffFunctions.DarkenClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenClear(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] = PorterDuffFunctions.DarkenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenClear(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] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenClear(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] = PorterDuffFunctions.DarkenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenClear(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] = PorterDuffFunctions.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenClear" composition equation. - /// - public class LightenClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenClear Instance { get; } = new LightenClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenClear(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] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenClear(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] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenClear(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] = PorterDuffFunctions.LightenClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenClear(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] = PorterDuffFunctions.LightenClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenClear(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] = PorterDuffFunctions.LightenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenClear(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] = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenClear(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] = PorterDuffFunctions.LightenClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenClear(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] = PorterDuffFunctions.LightenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayClear" composition equation. - /// - public class OverlayClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayClear Instance { get; } = new OverlayClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayClear(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] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayClear(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] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayClear(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] = PorterDuffFunctions.OverlayClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayClear(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] = PorterDuffFunctions.OverlayClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayClear(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] = PorterDuffFunctions.OverlayClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayClear(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] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayClear(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] = PorterDuffFunctions.OverlayClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayClear(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] = PorterDuffFunctions.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightClear" composition equation. - /// - public class HardLightClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightClear Instance { get; } = new HardLightClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightClear(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] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightClear(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] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightClear(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] = PorterDuffFunctions.HardLightClear(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightClear(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] = PorterDuffFunctions.HardLightClear(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightClear(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] = PorterDuffFunctions.HardLightClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightClear(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] = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightClear(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] = PorterDuffFunctions.HardLightClear(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightClear(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] = PorterDuffFunctions.HardLightClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightClear(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.HardLightClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalXor" composition equation. - /// - public class NormalXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalXor Instance { get; } = new NormalXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalXor(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] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalXor(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] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalXor(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] = PorterDuffFunctions.NormalXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalXor(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] = PorterDuffFunctions.NormalXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalXor(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] = PorterDuffFunctions.NormalXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalXor(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] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalXor(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] = PorterDuffFunctions.NormalXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalXor(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] = PorterDuffFunctions.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyXor" composition equation. - /// - public class MultiplyXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyXor Instance { get; } = new MultiplyXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyXor(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] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyXor(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] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyXor(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] = PorterDuffFunctions.MultiplyXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyXor(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] = PorterDuffFunctions.MultiplyXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyXor(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] = PorterDuffFunctions.MultiplyXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyXor(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] = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyXor(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] = PorterDuffFunctions.MultiplyXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyXor(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] = PorterDuffFunctions.MultiplyXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.MultiplyXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddXor" composition equation. - /// - public class AddXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddXor Instance { get; } = new AddXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddXor(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] = PorterDuffFunctions.AddXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddXor(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] = PorterDuffFunctions.AddXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddXor(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] = PorterDuffFunctions.AddXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddXor(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] = PorterDuffFunctions.AddXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddXor(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] = PorterDuffFunctions.AddXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddXor(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] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddXor(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] = PorterDuffFunctions.AddXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddXor(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] = PorterDuffFunctions.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractXor" composition equation. - /// - public class SubtractXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractXor Instance { get; } = new SubtractXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractXor(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] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractXor(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] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractXor(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] = PorterDuffFunctions.SubtractXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractXor(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] = PorterDuffFunctions.SubtractXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractXor(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] = PorterDuffFunctions.SubtractXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractXor(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] = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractXor(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] = PorterDuffFunctions.SubtractXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractXor(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] = PorterDuffFunctions.SubtractXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.SubtractXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.SubtractXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenXor" composition equation. - /// - public class ScreenXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenXor Instance { get; } = new ScreenXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenXor(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] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenXor(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] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenXor(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] = PorterDuffFunctions.ScreenXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenXor(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] = PorterDuffFunctions.ScreenXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenXor(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] = PorterDuffFunctions.ScreenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenXor(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] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenXor(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] = PorterDuffFunctions.ScreenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenXor(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] = PorterDuffFunctions.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenXor" composition equation. - /// - public class DarkenXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenXor Instance { get; } = new DarkenXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenXor(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] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenXor(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] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenXor(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] = PorterDuffFunctions.DarkenXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenXor(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] = PorterDuffFunctions.DarkenXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenXor(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] = PorterDuffFunctions.DarkenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenXor(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] = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenXor(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] = PorterDuffFunctions.DarkenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenXor(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] = PorterDuffFunctions.DarkenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.DarkenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.DarkenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenXor" composition equation. - /// - public class LightenXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenXor Instance { get; } = new LightenXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenXor(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] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenXor(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] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenXor(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] = PorterDuffFunctions.LightenXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenXor(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] = PorterDuffFunctions.LightenXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenXor(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] = PorterDuffFunctions.LightenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenXor(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] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenXor(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] = PorterDuffFunctions.LightenXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenXor(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] = PorterDuffFunctions.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "AddDest" composition equation. + /// + public sealed class AddDest : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddDest Instance { get; } = new(); } /// - /// A pixel blender that implements the "OverlayXor" composition equation. + /// Blends pixels with the "SubtractDest" composition equation. /// - public class OverlayXor : PixelBlender + public sealed class SubtractDest : + DefaultPixelBlender { /// - /// Gets the static instance of this blender. + /// Gets the shared blender instance. /// - public static OverlayXor Instance { get; } = new OverlayXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayXor(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] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayXor(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] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayXor(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] = PorterDuffFunctions.OverlayXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayXor(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] = PorterDuffFunctions.OverlayXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayXor(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] = PorterDuffFunctions.OverlayXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayXor(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] = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayXor(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] = PorterDuffFunctions.OverlayXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayXor(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] = PorterDuffFunctions.OverlayXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.OverlayXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.OverlayXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + public static SubtractDest Instance { get; } = new(); } /// - /// A pixel blender that implements the "HardLightXor" composition equation. + /// Blends pixels with the "ScreenDest" composition equation. /// - public class HardLightXor : PixelBlender + public sealed class ScreenDest : + DefaultPixelBlender { /// - /// Gets the static instance of this blender. + /// Gets the shared blender instance. /// - public static HardLightXor Instance { get; } = new HardLightXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightXor(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] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightXor(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] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightXor(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] = PorterDuffFunctions.HardLightXor(background[i], source, amount); - } - } - } - else if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightXor(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] = PorterDuffFunctions.HardLightXor(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightXor(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] = PorterDuffFunctions.HardLightXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightXor(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] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightXor(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] = PorterDuffFunctions.HardLightXor(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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); + public static ScreenDest Instance { get; } = new(); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "DarkenDest" composition equation. + /// + public sealed class DarkenDest : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenDest Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Blends pixels with the "LightenDest" composition equation. + /// + public sealed class LightenDest : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenDest Instance { get; } = new(); + } - destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } + /// + /// Blends pixels with the "OverlayDest" composition equation. + /// + public sealed class OverlayDest : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayDest Instance { get; } = new(); + } - 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] = PorterDuffFunctions.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "HardLightDest" composition equation. + /// + public sealed class HardLightDest : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightDest Instance { get; } = new(); + } - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + /// + /// Blends pixels with the "NormalDestAtop" composition equation. + /// + public sealed class NormalDestAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalDestAtop Instance { get; } = new(); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "MultiplyDestAtop" composition equation. + /// + public sealed class MultiplyDestAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyDestAtop Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + /// Blends pixels with the "AddDestAtop" composition equation. + /// + public sealed class AddDestAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddDestAtop Instance { get; } = new(); + } - 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); + /// + /// Blends pixels with the "SubtractDestAtop" composition equation. + /// + public sealed class SubtractDestAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractDestAtop Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "ScreenDestAtop" composition equation. + /// + public sealed class ScreenDestAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenDestAtop Instance { get; } = new(); + } - Vector512 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "DarkenDestAtop" composition equation. + /// + public sealed class DarkenDestAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenDestAtop Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "LightenDestAtop" composition equation. + /// + public sealed class LightenDestAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenDestAtop Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "OverlayDestAtop" composition equation. + /// + public sealed class OverlayDestAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayDestAtop Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "HardLightDestAtop" composition equation. + /// + public sealed class HardLightDestAtop : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightDestAtop Instance { get; } = new(); + } - Vector256 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "NormalDestOver" composition equation. + /// + public sealed class NormalDestOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalDestOver Instance { get; } = new(); + } - 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 = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "MultiplyDestOver" composition equation. + /// + public sealed class MultiplyDestOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyDestOver Instance { get; } = new(); + } - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) - { - amount = Numerics.Clamp(amount, 0, 1); + /// + /// Blends pixels with the "AddDestOver" composition equation. + /// + public sealed class AddDestOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddDestOver Instance { get; } = new(); + } - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "SubtractDestOver" composition equation. + /// + public sealed class SubtractDestOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractDestOver Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "ScreenDestOver" composition equation. + /// + public sealed class ScreenDestOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenDestOver Instance { get; } = new(); + } - Vector512 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 opacity = Vector512.Create(amount); - Vector512 vOne = Vector512.Create(1F); + /// + /// Blends pixels with the "DarkenDestOver" composition equation. + /// + public sealed class DarkenDestOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenDestOver Instance { get; } = new(); + } - 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); + /// + /// Blends pixels with the "LightenDestOver" composition equation. + /// + public sealed class LightenDestOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenDestOver Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "OverlayDestOver" composition equation. + /// + public sealed class OverlayDestOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayDestOver Instance { get; } = new(); + } - Vector512 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "HardLightDestOver" composition equation. + /// + public sealed class HardLightDestOver : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightDestOver Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "NormalDestIn" composition equation. + /// + public sealed class NormalDestIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalDestIn Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "MultiplyDestIn" composition equation. + /// + public sealed class MultiplyDestIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyDestIn Instance { get; } = new(); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "AddDestIn" composition equation. + /// + public sealed class AddDestIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddDestIn Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "SubtractDestIn" composition equation. + /// + public sealed class SubtractDestIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractDestIn Instance { get; } = new(); + } - Vector256 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "ScreenDestIn" composition equation. + /// + public sealed class ScreenDestIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenDestIn Instance { get; } = new(); + } - 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 = PorterDuffFunctions.HardLightXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "DarkenDestIn" composition equation. + /// + public sealed class DarkenDestIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenDestIn Instance { get; } = new(); + } - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "LightenDestIn" composition equation. + /// + public sealed class LightenDestIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenDestIn Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "OverlayDestIn" composition equation. + /// + public sealed class OverlayDestIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayDestIn Instance { get; } = new(); + } - Vector512 vOne = Vector512.Create(1F); + /// + /// Blends pixels with the "HardLightDestIn" composition equation. + /// + public sealed class HardLightDestIn : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightDestIn Instance { get; } = new(); + } - 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); + /// + /// Blends pixels with the "NormalDestOut" composition equation. + /// + public sealed class NormalDestOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalDestOut Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + /// Blends pixels with the "MultiplyDestOut" composition equation. + /// + public sealed class MultiplyDestOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyDestOut Instance { get; } = new(); + } - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + /// Blends pixels with the "AddDestOut" composition equation. + /// + public sealed class AddDestOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddDestOut Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "SubtractDestOut" composition equation. + /// + public sealed class SubtractDestOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractDestOut Instance { get; } = new(); + } - Vector512 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "ScreenDestOut" composition equation. + /// + public sealed class ScreenDestOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenDestOut Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "DarkenDestOut" composition equation. + /// + public sealed class DarkenDestOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenDestOut Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "LightenDestOut" composition equation. + /// + public sealed class LightenDestOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenDestOut Instance { get; } = new(); + } - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "OverlayDestOut" composition equation. + /// + public sealed class OverlayDestOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayDestOut Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Blends pixels with the "HardLightDestOut" composition equation. + /// + public sealed class HardLightDestOut : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightDestOut Instance { get; } = new(); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "NormalClear" composition equation. + /// + public sealed class NormalClear : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalClear Instance { get; } = new(); + } - Vector256 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "MultiplyClear" composition equation. + /// + public sealed class MultiplyClear : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyClear Instance { get; } = new(); + } - 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 = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "AddClear" composition equation. + /// + public sealed class AddClear : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddClear Instance { get; } = new(); + } - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + /// + /// Blends pixels with the "SubtractClear" composition equation. + /// + public sealed class SubtractClear : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractClear Instance { get; } = new(); + } - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "ScreenClear" composition equation. + /// + public sealed class ScreenClear : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenClear Instance { get; } = new(); + } - Vector512 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 vOne = Vector512.Create(1F); + /// + /// Blends pixels with the "DarkenClear" composition equation. + /// + public sealed class DarkenClear : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenClear Instance { get; } = new(); + } - 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); + /// + /// Blends pixels with the "LightenClear" composition equation. + /// + public sealed class LightenClear : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenClear Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); + /// + /// Blends pixels with the "OverlayClear" composition equation. + /// + public sealed class OverlayClear : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayClear Instance { get; } = new(); + } - float coverage0 = coverageBase; - float coverage1 = Unsafe.Add(ref coverageBase, 1); - float coverage2 = Unsafe.Add(ref coverageBase, 2); - float coverage3 = Unsafe.Add(ref coverageBase, 3); + /// + /// Blends pixels with the "HardLightClear" composition equation. + /// + public sealed class HardLightClear : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightClear Instance { get; } = new(); + } - // We need to create a Vector512 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "NormalXor" composition equation. + /// + public sealed class NormalXor : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static NormalXor Instance { get; } = new(); + } - Vector512 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "MultiplyXor" composition equation. + /// + public sealed class MultiplyXor : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static MultiplyXor Instance { get; } = new(); + } - int remainder = Numerics.Modulo4(destination.Length); - if (remainder != 0) - { - for (int i = destination.Length - remainder; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + /// + /// Blends pixels with the "AddXor" composition equation. + /// + public sealed class AddXor : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static AddXor Instance { get; } = new(); + } - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + /// + /// Blends pixels with the "SubtractXor" composition equation. + /// + public sealed class SubtractXor : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static SubtractXor Instance { get; } = new(); + } - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); + /// + /// Blends pixels with the "ScreenXor" composition equation. + /// + public sealed class ScreenXor : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static ScreenXor Instance { get; } = new(); + } - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + /// + /// Blends pixels with the "DarkenXor" composition equation. + /// + public sealed class DarkenXor : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static DarkenXor Instance { get; } = new(); + } - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + /// + /// Blends pixels with the "LightenXor" composition equation. + /// + public sealed class LightenXor : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static LightenXor Instance { get; } = new(); + } - Vector256 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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); - } + /// + /// Blends pixels with the "OverlayXor" composition equation. + /// + public sealed class OverlayXor : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static OverlayXor Instance { get; } = new(); + } - 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 = PorterDuffFunctions.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } + /// + /// Blends pixels with the "HardLightXor" composition equation. + /// + public sealed class HardLightXor : + DefaultPixelBlender + { + /// + /// Gets the shared blender instance. + /// + public static HardLightXor Instance { get; } = new(); } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index b1d8134ce..99610b7d8 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -4,39 +4,19 @@ #> <#@ 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. // 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; -/// -/// Collection of Porter Duff alpha blending functions applying different composition models. -/// -/// -/// These functions are designed to be a general solution for all color cases, -/// that is, they take in account the alpha value of both the backdrop -/// and source, and there's no need to alpha-premultiply neither the backdrop -/// nor the source. -/// Note there are faster functions for when the backdrop color is known -/// to be opaque -/// -internal static class DefaultPixelBlenders - where TPixel : unmanaged, IPixel -{ - <# -var composers = new []{ +var composers = new [] +{ "Src", "SrcAtop", "SrcOver", @@ -51,7 +31,8 @@ var composers = new []{ "Xor", }; -var blenders = new []{ +var blenders = new [] +{ "Normal", "Multiply", "Add", @@ -60,796 +41,79 @@ var blenders = new []{ "Darken", "Lighten", "Overlay", - "HardLight" + "HardLight", }; - - foreach(var composer in composers) { - foreach(var blender in blenders) { - - var blender_composer= $"{blender}{composer}"; +#> +/// +/// Provides the straight-alpha equations consumed by the shared pixel-blending traversal. +/// +internal static class DefaultPixelBlenderOperators +{ +<# +foreach (var composer in composers) +{ + foreach (var blender in blenders) + { + var blenderComposer = $"{blender}{composer}"; #> /// - /// A pixel blender that implements the "<#= blender_composer#>" composition equation. + /// Applies the "<#= blenderComposer #>" straight-alpha composition equation. /// - public class <#= blender_composer#> : PixelBlender + public readonly struct <#= blenderComposer #> : IPixelBlenderOperator { - /// - /// Gets the static instance of this blender. - /// - public static <#=blender_composer#> Instance { get; } = new <#=blender_composer#>(); - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.<#=blender_composer#>(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.<#= blenderComposer #>(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector512 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.<#=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] = PorterDuffFunctions.<#=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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.<#=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] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); - } - } - } + public static Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount) + => PorterDuffFunctions.<#= blenderComposer #>(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector512 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 opacity = Vector512.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.<#=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] = PorterDuffFunctions.<#=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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.<#=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] = PorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.<#=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] = PorterDuffFunctions.<#=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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.<#=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] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.<#=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] = PorterDuffFunctions.<#=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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.<#=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] = PorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector512 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan 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 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 opacity = Vector512.Create(amount); - Vector512 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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 opacity = Vector256.Create(amount); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); - - Vector256 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } - - /// - protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) - { - if (Avx512F.IsSupported && destination.Length >= 4) - { - // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 - ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); - - ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector512 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 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 containing the current four amount values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.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 containing the current four coverage values - // taking up each quarter of the Vector512 and then clamp them. - Vector512 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.Zero, coverageVector), vOne); - - Vector512 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.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 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - ref float coverageBase = ref MemoryMarshal.GetReference(coverage); - - Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - // We need to create a Vector256 containing the current and next coverage values - // taking up each half of the Vector256 and then clamp them. - Vector256 coverageVector = Vector256.Create( - Vector128.Create(coverageBase), - Vector128.Create(Unsafe.Add(ref coverageBase, 1))); - coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + public static Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount) + => PorterDuffFunctions.<#= blenderComposer #>(background, source, amount); + } - Vector256 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); - destinationBase = PorterDuffFunctions.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 = PorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); - destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); - } - } - } +/// +/// Provides straight-alpha pixel blenders for each color and alpha composition pair. +/// +/// The destination pixel format. +internal static class DefaultPixelBlenders + where TPixel : unmanaged, IPixel +{ +<# +foreach (var composer in composers) +{ + foreach (var blender in blenders) + { + var blenderComposer = $"{blender}{composer}"; +#> + /// + /// Blends pixels with the "<#= blenderComposer #>" composition equation. + /// + public sealed class <#= blenderComposer #> : + DefaultPixelBlender> + { + /// + /// Gets the shared blender instance. + /// + public static <#= blenderComposer #> Instance { get; } = new(); } <# } } - #> } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/IPixelBlenderOperator.cs b/src/ImageSharp/PixelFormats/PixelBlenders/IPixelBlenderOperator.cs new file mode 100644 index 000000000..bdb43e7be --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/IPixelBlenderOperator.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +/// +/// Defines a Porter-Duff equation that can be applied by the shared pixel-blending traversal. +/// +internal interface IPixelBlenderOperator +{ + /// + /// Blends one pixel represented by four RGBA lanes. + /// + /// The background RGBA lanes. + /// The source RGBA lanes. + /// The source opacity in the range 0 through 1. + /// The blended RGBA lanes. + static abstract Vector4 Invoke(Vector4 background, Vector4 source, float amount); + + /// + /// Blends two pixels represented by two consecutive groups of four RGBA lanes. + /// + /// The background RGBA lanes. + /// The source RGBA lanes. + /// The source opacity repeated across each pixel's four lanes. + /// The blended RGBA lanes. + static abstract Vector256 Invoke( + Vector256 background, + Vector256 source, + Vector256 amount); + + /// + /// Blends four pixels represented by four consecutive groups of four RGBA lanes. + /// + /// The background RGBA lanes. + /// The source RGBA lanes. + /// The source opacity repeated across each pixel's four lanes. + /// The blended RGBA lanes. + static abstract Vector512 Invoke( + Vector512 background, + Vector512 source, + Vector512 amount); +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PixelBlender{TPixel,TOperator}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PixelBlender{TPixel,TOperator}.cs new file mode 100644 index 000000000..f50151256 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PixelBlender{TPixel,TOperator}.cs @@ -0,0 +1,706 @@ +// 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 System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +/// +/// Applies a statically selected Porter-Duff equation across pixel-vector rows. +/// +/// The destination pixel format. +/// The Porter-Duff equation. +internal abstract class PixelBlender : PixelBlender + where TPixel : unmanaged, IPixel + where TOperator : struct, IPixelBlenderOperator +{ + /// + protected sealed override void BlendFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount) + { + // Public entry points validate the row lengths, so all three references can advance in lockstep. + int scalarStart = 0; + amount = Numerics.Clamp(amount, 0, 1F); + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(source); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // A 512-bit register holds four complete RGBA pixels in [R,G,B,A] groups. + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector512 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 4; + Vector512 amountVector = Vector512.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke( + Unsafe.Add(ref backgroundBase, i), + Unsafe.Add(ref sourceBase, i), + amountVector); + } + + scalarStart = vectorCount * 4; + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // A 256-bit register holds two complete RGBA pixels in [R,G,B,A] groups. + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector256 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 2; + Vector256 amountVector = Vector256.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke( + Unsafe.Add(ref backgroundBase, i), + Unsafe.Add(ref sourceBase, i), + amountVector); + } + + scalarStart = vectorCount * 2; + } + + // Vector4 is both the scalar pixel representation and the portable SIMD fallback. + for (int i = scalarStart; i < destination.Length; i++) + { + Unsafe.Add(ref destinationRef, (uint)i) = TOperator.Invoke( + Unsafe.Add(ref backgroundRef, (uint)i), + Unsafe.Add(ref sourceRef, (uint)i), + amount); + } + } + + /// + protected sealed override void BlendFunction( + Span destination, + ReadOnlySpan background, + Vector4 source, + float amount) + { + // Public entry points validate the row lengths, so the destination and background advance together. + int scalarStart = 0; + amount = Numerics.Clamp(amount, 0, 1F); + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Repeat one [R,G,B,A] source group four times to match the four background pixels. + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 4; + Vector512 sourceVector = CreateVector512(source); + Vector512 amountVector = Vector512.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke( + Unsafe.Add(ref backgroundBase, i), + sourceVector, + amountVector); + } + + scalarStart = vectorCount * 4; + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Repeat one [R,G,B,A] source group twice to match the two background pixels. + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 2; + Vector256 sourceVector = CreateVector256(source); + Vector256 amountVector = Vector256.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke( + Unsafe.Add(ref backgroundBase, i), + sourceVector, + amountVector); + } + + scalarStart = vectorCount * 2; + } + + // The remaining pixel count is at most three after AVX-512 or one after AVX2. + for (int i = scalarStart; i < destination.Length; i++) + { + Unsafe.Add(ref destinationRef, (uint)i) = TOperator.Invoke( + Unsafe.Add(ref backgroundRef, (uint)i), + source, + amount); + } + } + + /// + protected sealed override void BlendFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount) + { + // Each amount belongs to one pixel and must be repeated across that pixel's four RGBA lanes. + int scalarStart = 0; + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(source); + ref float amountRef = ref MemoryMarshal.GetReference(amount); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector512 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 4; + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + // Four scalar amounts become four contiguous [a,a,a,a] groups before clamping. + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 4); + Vector512 amountVector = CreateClampedVector512(ref amountBase); + + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke( + Unsafe.Add(ref backgroundBase, i), + Unsafe.Add(ref sourceBase, i), + amountVector); + } + + scalarStart = vectorCount * 4; + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector256 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 2; + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + // Two scalar amounts become two contiguous [a,a,a,a] groups before clamping. + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 2); + Vector256 amountVector = CreateClampedVector256(ref amountBase); + + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke( + Unsafe.Add(ref backgroundBase, i), + Unsafe.Add(ref sourceBase, i), + amountVector); + } + + scalarStart = vectorCount * 2; + } + + for (int i = scalarStart; i < destination.Length; i++) + { + Unsafe.Add(ref destinationRef, (uint)i) = TOperator.Invoke( + Unsafe.Add(ref backgroundRef, (uint)i), + Unsafe.Add(ref sourceRef, (uint)i), + Numerics.Clamp(Unsafe.Add(ref amountRef, (uint)i), 0, 1F)); + } + } + + /// + protected sealed override void BlendFunction( + Span destination, + ReadOnlySpan background, + Vector4 source, + ReadOnlySpan amount) + { + // The source is invariant, while each background pixel has its own independently clamped amount. + int scalarStart = 0; + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref float amountRef = ref MemoryMarshal.GetReference(amount); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 4; + Vector512 sourceVector = CreateVector512(source); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 4); + Vector512 amountVector = CreateClampedVector512(ref amountBase); + + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke( + Unsafe.Add(ref backgroundBase, i), + sourceVector, + amountVector); + } + + scalarStart = vectorCount * 4; + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 2; + Vector256 sourceVector = CreateVector256(source); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 2); + Vector256 amountVector = CreateClampedVector256(ref amountBase); + + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke( + Unsafe.Add(ref backgroundBase, i), + sourceVector, + amountVector); + } + + scalarStart = vectorCount * 2; + } + + for (int i = scalarStart; i < destination.Length; i++) + { + Unsafe.Add(ref destinationRef, (uint)i) = TOperator.Invoke( + Unsafe.Add(ref backgroundRef, (uint)i), + source, + Numerics.Clamp(Unsafe.Add(ref amountRef, (uint)i), 0, 1F)); + } + } + + /// + protected sealed override void BlendWithCoverageFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount, + ReadOnlySpan coverage) + { + // Coverage mixes the composed result back toward the original background, so it is fused into this pass. + int scalarStart = 0; + amount = Numerics.Clamp(amount, 0, 1F); + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(source); + ref float coverageRef = ref MemoryMarshal.GetReference(coverage); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector512 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 4; + Vector512 amountVector = Vector512.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector512 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 4); + Vector512 coverageVector = CreateClampedVector512(ref coverageBase); + Vector512 blended = TOperator.Invoke( + backgroundVector, + Unsafe.Add(ref sourceBase, i), + amountVector); + + Unsafe.Add(ref destinationBase, i) = PorterDuffFunctions.BlendWithCoverage( + backgroundVector, + blended, + coverageVector); + } + + scalarStart = vectorCount * 4; + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector256 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 2; + Vector256 amountVector = Vector256.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector256 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 2); + Vector256 coverageVector = CreateClampedVector256(ref coverageBase); + Vector256 blended = TOperator.Invoke( + backgroundVector, + Unsafe.Add(ref sourceBase, i), + amountVector); + + Unsafe.Add(ref destinationBase, i) = PorterDuffFunctions.BlendWithCoverage( + backgroundVector, + blended, + coverageVector); + } + + scalarStart = vectorCount * 2; + } + + for (int i = scalarStart; i < destination.Length; i++) + { + Vector4 backgroundPixel = Unsafe.Add(ref backgroundRef, (uint)i); + Vector4 blended = TOperator.Invoke( + backgroundPixel, + Unsafe.Add(ref sourceRef, (uint)i), + amount); + + Unsafe.Add(ref destinationRef, (uint)i) = PorterDuffFunctions.BlendWithCoverage( + backgroundPixel, + blended, + Numerics.Clamp(Unsafe.Add(ref coverageRef, (uint)i), 0, 1F)); + } + } + + /// + protected sealed override void BlendWithCoverageFunction( + Span destination, + ReadOnlySpan background, + Vector4 source, + float amount, + ReadOnlySpan coverage) + { + // The constant source is expanded once per selected width and reused for the complete row. + int scalarStart = 0; + amount = Numerics.Clamp(amount, 0, 1F); + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref float coverageRef = ref MemoryMarshal.GetReference(coverage); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 4; + Vector512 sourceVector = CreateVector512(source); + Vector512 amountVector = Vector512.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector512 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 4); + Vector512 coverageVector = CreateClampedVector512(ref coverageBase); + Vector512 blended = TOperator.Invoke(backgroundVector, sourceVector, amountVector); + + Unsafe.Add(ref destinationBase, i) = PorterDuffFunctions.BlendWithCoverage( + backgroundVector, + blended, + coverageVector); + } + + scalarStart = vectorCount * 4; + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 2; + Vector256 sourceVector = CreateVector256(source); + Vector256 amountVector = Vector256.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector256 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 2); + Vector256 coverageVector = CreateClampedVector256(ref coverageBase); + Vector256 blended = TOperator.Invoke(backgroundVector, sourceVector, amountVector); + + Unsafe.Add(ref destinationBase, i) = PorterDuffFunctions.BlendWithCoverage( + backgroundVector, + blended, + coverageVector); + } + + scalarStart = vectorCount * 2; + } + + for (int i = scalarStart; i < destination.Length; i++) + { + Vector4 backgroundPixel = Unsafe.Add(ref backgroundRef, (uint)i); + Vector4 blended = TOperator.Invoke(backgroundPixel, source, amount); + + Unsafe.Add(ref destinationRef, (uint)i) = PorterDuffFunctions.BlendWithCoverage( + backgroundPixel, + blended, + Numerics.Clamp(Unsafe.Add(ref coverageRef, (uint)i), 0, 1F)); + } + } + + /// + protected sealed override void BlendWithCoverageFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + { + // Amount controls composition while coverage controls the final mix with the untouched background. + int scalarStart = 0; + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(source); + ref float amountRef = ref MemoryMarshal.GetReference(amount); + ref float coverageRef = ref MemoryMarshal.GetReference(coverage); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector512 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 4; + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector512 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 4); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 4); + Vector512 amountVector = CreateClampedVector512(ref amountBase); + Vector512 coverageVector = CreateClampedVector512(ref coverageBase); + Vector512 blended = TOperator.Invoke( + backgroundVector, + Unsafe.Add(ref sourceBase, i), + amountVector); + + Unsafe.Add(ref destinationBase, i) = PorterDuffFunctions.BlendWithCoverage( + backgroundVector, + blended, + coverageVector); + } + + scalarStart = vectorCount * 4; + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector256 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 2; + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector256 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 2); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 2); + Vector256 amountVector = CreateClampedVector256(ref amountBase); + Vector256 coverageVector = CreateClampedVector256(ref coverageBase); + Vector256 blended = TOperator.Invoke( + backgroundVector, + Unsafe.Add(ref sourceBase, i), + amountVector); + + Unsafe.Add(ref destinationBase, i) = PorterDuffFunctions.BlendWithCoverage( + backgroundVector, + blended, + coverageVector); + } + + scalarStart = vectorCount * 2; + } + + for (int i = scalarStart; i < destination.Length; i++) + { + Vector4 backgroundPixel = Unsafe.Add(ref backgroundRef, (uint)i); + Vector4 blended = TOperator.Invoke( + backgroundPixel, + Unsafe.Add(ref sourceRef, (uint)i), + Numerics.Clamp(Unsafe.Add(ref amountRef, (uint)i), 0, 1F)); + + Unsafe.Add(ref destinationRef, (uint)i) = PorterDuffFunctions.BlendWithCoverage( + backgroundPixel, + blended, + Numerics.Clamp(Unsafe.Add(ref coverageRef, (uint)i), 0, 1F)); + } + } + + /// + protected sealed override void BlendWithCoverageFunction( + Span destination, + ReadOnlySpan background, + Vector4 source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + { + // The invariant source is expanded once; only amount and coverage are gathered for each vector batch. + int scalarStart = 0; + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref float amountRef = ref MemoryMarshal.GetReference(amount); + ref float coverageRef = ref MemoryMarshal.GetReference(coverage); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 4; + Vector512 sourceVector = CreateVector512(source); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector512 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 4); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 4); + Vector512 amountVector = CreateClampedVector512(ref amountBase); + Vector512 coverageVector = CreateClampedVector512(ref coverageBase); + Vector512 blended = TOperator.Invoke(backgroundVector, sourceVector, amountVector); + + Unsafe.Add(ref destinationBase, i) = PorterDuffFunctions.BlendWithCoverage( + backgroundVector, + blended, + coverageVector); + } + + scalarStart = vectorCount * 4; + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 2; + Vector256 sourceVector = CreateVector256(source); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector256 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 2); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 2); + Vector256 amountVector = CreateClampedVector256(ref amountBase); + Vector256 coverageVector = CreateClampedVector256(ref coverageBase); + Vector256 blended = TOperator.Invoke(backgroundVector, sourceVector, amountVector); + + Unsafe.Add(ref destinationBase, i) = PorterDuffFunctions.BlendWithCoverage( + backgroundVector, + blended, + coverageVector); + } + + scalarStart = vectorCount * 2; + } + + for (int i = scalarStart; i < destination.Length; i++) + { + Vector4 backgroundPixel = Unsafe.Add(ref backgroundRef, (uint)i); + Vector4 blended = TOperator.Invoke( + backgroundPixel, + source, + Numerics.Clamp(Unsafe.Add(ref amountRef, (uint)i), 0, 1F)); + + Unsafe.Add(ref destinationRef, (uint)i) = PorterDuffFunctions.BlendWithCoverage( + backgroundPixel, + blended, + Numerics.Clamp(Unsafe.Add(ref coverageRef, (uint)i), 0, 1F)); + } + } + + /// + /// Repeats one RGBA pixel twice to fill a 256-bit register. + /// + /// The pixel represented by ordered RGBA lanes. + /// Two consecutive copies of the pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 CreateVector256(Vector4 pixel) + => Vector256.Create(pixel.X, pixel.Y, pixel.Z, pixel.W, pixel.X, pixel.Y, pixel.Z, pixel.W); + + /// + /// Repeats one RGBA pixel four times to fill a 512-bit register. + /// + /// The pixel represented by ordered RGBA lanes. + /// Four consecutive copies of the pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 CreateVector512(Vector4 pixel) + => Vector512.Create( + pixel.X, + pixel.Y, + pixel.Z, + pixel.W, + pixel.X, + pixel.Y, + pixel.Z, + pixel.W, + pixel.X, + pixel.Y, + pixel.Z, + pixel.W, + pixel.X, + pixel.Y, + pixel.Z, + pixel.W); + + /// + /// Expands and clamps two per-pixel scalar values for two packed RGBA pixels. + /// + /// The first of two contiguous scalar values. + /// Two consecutive groups containing one clamped value in each group's four lanes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 CreateClampedVector256(ref float values) + { + Vector256 result = Vector256.Create( + Vector128.Create(values), + Vector128.Create(Unsafe.Add(ref values, 1))); + + // Amount and coverage share the same public 0..1 contract and therefore the same packed clamp. + return Avx.Min(Avx.Max(Vector256.Zero, result), Vector256.Create(1F)); + } + + /// + /// Expands and clamps four per-pixel scalar values for four packed RGBA pixels. + /// + /// The first of four contiguous scalar values. + /// Four consecutive groups containing one clamped value in each group's four lanes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 CreateClampedVector512(ref float values) + { + Vector512 result = Vector512.Create( + values, + values, + values, + values, + Unsafe.Add(ref values, 1), + Unsafe.Add(ref values, 1), + Unsafe.Add(ref values, 1), + Unsafe.Add(ref values, 1), + Unsafe.Add(ref values, 2), + Unsafe.Add(ref values, 2), + Unsafe.Add(ref values, 2), + Unsafe.Add(ref values, 2), + Unsafe.Add(ref values, 3), + Unsafe.Add(ref values, 3), + Unsafe.Add(ref values, 3), + Unsafe.Add(ref values, 3)); + + return Vector512.Min(Vector512.Max(Vector512.Zero, result), Vector512.Create(1F)); + } +} + +/// +/// Applies a statically selected Porter-Duff equation to straight-alpha pixels. +/// +/// The straight-alpha pixel format. +/// The Porter-Duff equation. +internal abstract class DefaultPixelBlender : PixelBlender + where TPixel : unmanaged, IPixel + where TOperator : struct, IPixelBlenderOperator +{ + /// + public sealed override TPixel Blend(TPixel background, TPixel source, float amount) + { + // The operator consumes the same unassociated, scaled representation used by the bulk conversion path. + Vector4 result = TOperator.Invoke( + background.ToUnassociatedScaledVector4(), + source.ToUnassociatedScaledVector4(), + Numerics.Clamp(amount, 0, 1F)); + + return TPixel.FromUnassociatedScaledVector4(result); + } +} diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PixelBlenderTraversalAssembly.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PixelBlenderTraversalAssembly.cs new file mode 100644 index 000000000..a8d6095c7 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PixelBlenderTraversalAssembly.cs @@ -0,0 +1,327 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +namespace SixLabors.ImageSharp.Benchmarks.PixelBlenders; + +/// +/// Exposes every shared pixel-blender traversal shape for assembly inspection. +/// +/// +/// Seven pixels leave three scalar pixels after AVX-512 or one scalar pixel after AVX2, so each +/// hardware job contains both its widest supported loop and the portable Vector4 remainder. +/// +[Config(typeof(Config.Analysis))] +public class PixelBlenderTraversalAssembly +{ + private const int Count = 7; + private const float Amount = .625F; + + private readonly ExposedNormalSrcOverBlender blender = new(); + private readonly Vector4[] destination = new Vector4[Count]; + private readonly Vector4[] background = new Vector4[Count]; + private readonly Vector4[] source = new Vector4[Count]; + private readonly float[] amounts = new float[Count]; + private readonly float[] coverage = new float[Count]; + private Vector4 constantSource; + + /// + /// Populates all lanes with deterministic, non-constant values. + /// + [GlobalSetup] + public void Setup() + { + Random random = new(42); + + for (int i = 0; i < Count; i++) + { + // Distinct RGBA lanes make incorrect pixel grouping visible in both results and assembly. + this.background[i] = CreatePixel(random); + this.source[i] = CreatePixel(random); + this.amounts[i] = random.NextSingle(); + this.coverage[i] = random.NextSingle(); + } + + this.constantSource = CreatePixel(random); + } + + /// + /// Blends a source row with one shared amount. + /// + /// The final destination pixel. + [Benchmark] + public Vector4 SourceSpanScalarAmount() + { + this.blender.BlendSourceSpanScalarAmount( + this.destination, + this.background, + this.source, + Amount); + + return this.destination[^1]; + } + + /// + /// Blends a constant source with one shared amount. + /// + /// The final destination pixel. + [Benchmark] + public Vector4 ConstantSourceScalarAmount() + { + this.blender.BlendConstantSourceScalarAmount( + this.destination, + this.background, + this.constantSource, + Amount); + + return this.destination[^1]; + } + + /// + /// Blends a source row with per-pixel amounts. + /// + /// The final destination pixel. + [Benchmark] + public Vector4 SourceSpanAmountSpan() + { + this.blender.BlendSourceSpanAmountSpan( + this.destination, + this.background, + this.source, + this.amounts); + + return this.destination[^1]; + } + + /// + /// Blends a constant source with per-pixel amounts. + /// + /// The final destination pixel. + [Benchmark] + public Vector4 ConstantSourceAmountSpan() + { + this.blender.BlendConstantSourceAmountSpan( + this.destination, + this.background, + this.constantSource, + this.amounts); + + return this.destination[^1]; + } + + /// + /// Blends a source row with one shared amount and per-pixel coverage. + /// + /// The final destination pixel. + [Benchmark] + public Vector4 SourceSpanScalarAmountCoverage() + { + this.blender.BlendSourceSpanScalarAmountCoverage( + this.destination, + this.background, + this.source, + Amount, + this.coverage); + + return this.destination[^1]; + } + + /// + /// Blends a constant source with one shared amount and per-pixel coverage. + /// + /// The final destination pixel. + [Benchmark] + public Vector4 ConstantSourceScalarAmountCoverage() + { + this.blender.BlendConstantSourceScalarAmountCoverage( + this.destination, + this.background, + this.constantSource, + Amount, + this.coverage); + + return this.destination[^1]; + } + + /// + /// Blends a source row with per-pixel amounts and coverage. + /// + /// The final destination pixel. + [Benchmark] + public Vector4 SourceSpanAmountSpanCoverage() + { + this.blender.BlendSourceSpanAmountSpanCoverage( + this.destination, + this.background, + this.source, + this.amounts, + this.coverage); + + return this.destination[^1]; + } + + /// + /// Blends a constant source with per-pixel amounts and coverage. + /// + /// The final destination pixel. + [Benchmark] + public Vector4 ConstantSourceAmountSpanCoverage() + { + this.blender.BlendConstantSourceAmountSpanCoverage( + this.destination, + this.background, + this.constantSource, + this.amounts, + this.coverage); + + return this.destination[^1]; + } + + /// + /// Creates one non-constant RGBA sample. + /// + /// The deterministic value source. + /// The sample pixel. + private static Vector4 CreatePixel(Random random) + => new(random.NextSingle(), random.NextSingle(), random.NextSingle(), random.NextSingle()); + + /// + /// Exposes the protected shared traversal overloads without adding benchmark hooks to production APIs. + /// + private sealed class ExposedNormalSrcOverBlender : + DefaultPixelBlender + { + /// + /// Invokes the source-span, scalar-amount traversal. + /// + /// The destination vectors. + /// The background vectors. + /// The source vectors. + /// The shared source amount. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BlendSourceSpanScalarAmount( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount) + => this.BlendFunction(destination, background, source, amount); + + /// + /// Invokes the constant-source, scalar-amount traversal. + /// + /// The destination vectors. + /// The background vectors. + /// The constant source vector. + /// The shared source amount. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BlendConstantSourceScalarAmount( + Span destination, + ReadOnlySpan background, + Vector4 source, + float amount) + => this.BlendFunction(destination, background, source, amount); + + /// + /// Invokes the source-span, amount-span traversal. + /// + /// The destination vectors. + /// The background vectors. + /// The source vectors. + /// The per-pixel source amounts. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BlendSourceSpanAmountSpan( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount) + => this.BlendFunction(destination, background, source, amount); + + /// + /// Invokes the constant-source, amount-span traversal. + /// + /// The destination vectors. + /// The background vectors. + /// The constant source vector. + /// The per-pixel source amounts. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BlendConstantSourceAmountSpan( + Span destination, + ReadOnlySpan background, + Vector4 source, + ReadOnlySpan amount) + => this.BlendFunction(destination, background, source, amount); + + /// + /// Invokes the source-span, scalar-amount traversal with coverage. + /// + /// The destination vectors. + /// The background vectors. + /// The source vectors. + /// The shared source amount. + /// The per-pixel coverage values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BlendSourceSpanScalarAmountCoverage( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount, + ReadOnlySpan coverage) + => this.BlendWithCoverageFunction(destination, background, source, amount, coverage); + + /// + /// Invokes the constant-source, scalar-amount traversal with coverage. + /// + /// The destination vectors. + /// The background vectors. + /// The constant source vector. + /// The shared source amount. + /// The per-pixel coverage values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BlendConstantSourceScalarAmountCoverage( + Span destination, + ReadOnlySpan background, + Vector4 source, + float amount, + ReadOnlySpan coverage) + => this.BlendWithCoverageFunction(destination, background, source, amount, coverage); + + /// + /// Invokes the source-span, amount-span traversal with coverage. + /// + /// The destination vectors. + /// The background vectors. + /// The source vectors. + /// The per-pixel source amounts. + /// The per-pixel coverage values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BlendSourceSpanAmountSpanCoverage( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + => this.BlendWithCoverageFunction(destination, background, source, amount, coverage); + + /// + /// Invokes the constant-source, amount-span traversal with coverage. + /// + /// The destination vectors. + /// The background vectors. + /// The constant source vector. + /// The per-pixel source amounts. + /// The per-pixel coverage values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BlendConstantSourceAmountSpanCoverage( + Span destination, + ReadOnlySpan background, + Vector4 source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + => this.BlendWithCoverageFunction(destination, background, source, amount, coverage); + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs index a851e2fcf..448de9ec4 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs @@ -791,42 +791,104 @@ public class PixelBlenderTests ExerciseBlender(blender, background, source); } + /// + /// Exercises every bulk overload across vector-width boundaries and compares it with single-pixel composition. + /// + /// The pixel format. + /// The blender under test. + /// The background pixel. + /// The source pixel. private static void ExerciseBlender(PixelBlender blender, TPixel background, TPixel source) where TPixel : unmanaged, IPixel { - float[] amount = [1F, 1F, 1F, 1F]; - float[] coverage = [1F, 1F, 1F, 1F]; + const float amount = .625F; - TPixel expected = blender.Blend(background, source, 1F); + float[] amounts = [0F, .125F, .375F, .625F, .875F, 1F, .5F]; + float[] coverage = [1F, .8F, .6F, .4F, .2F, 0F, .5F]; - TPixel[] destination = new TPixel[4]; - TPixel[] backgroundSpan = [background, background, background, background]; - TPixel[] sourceSpan = [source, source, source, source]; + // Seven pixels exercise one AVX-512 batch plus three tails, or three AVX2 batches plus one tail. + TPixel[] destination = new TPixel[7]; + TPixel[] expected = new TPixel[7]; + TPixel[] backgroundSpan = [background, background, background, background, background, background, background]; + TPixel[] sourceSpan = [source, source, source, source, source, source, source]; Vector4[] sourceSpanBuffer = new Vector4[destination.Length * 3]; Vector4[] constantSourceBuffer = new Vector4[destination.Length * 2]; - blender.Blend(Configuration.Default, destination, backgroundSpan, sourceSpan, 1F, sourceSpanBuffer); - Assert.All(destination, x => Assert.Equal(expected, x)); - - blender.Blend(Configuration.Default, destination, backgroundSpan, source, 1F, constantSourceBuffer); - Assert.All(destination, x => Assert.Equal(expected, x)); + Array.Fill(expected, blender.Blend(background, source, amount)); - blender.Blend(Configuration.Default, destination, backgroundSpan, sourceSpan, amount, sourceSpanBuffer); - Assert.All(destination, x => Assert.Equal(expected, x)); + blender.Blend(Configuration.Default, destination, backgroundSpan, sourceSpan, amount, sourceSpanBuffer); + Assert.Equal(expected, destination); blender.Blend(Configuration.Default, destination, backgroundSpan, source, amount, constantSourceBuffer); - Assert.All(destination, x => Assert.Equal(expected, x)); + Assert.Equal(expected, destination); - blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, sourceSpan, 1F, coverage, sourceSpanBuffer); - Assert.All(destination, x => Assert.Equal(expected, x)); + for (int i = 0; i < expected.Length; i++) + { + expected[i] = blender.Blend(background, source, amounts[i]); + } - blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, source, 1F, coverage, constantSourceBuffer); - Assert.All(destination, x => Assert.Equal(expected, x)); + blender.Blend(Configuration.Default, destination, backgroundSpan, sourceSpan, amounts, sourceSpanBuffer); + Assert.Equal(expected, destination); - blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, sourceSpan, amount, coverage, sourceSpanBuffer); - Assert.All(destination, x => Assert.Equal(expected, x)); + blender.Blend(Configuration.Default, destination, backgroundSpan, source, amounts, constantSourceBuffer); + Assert.Equal(expected, destination); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = BlendWithCoverageScalar(blender, background, source, amount, coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, sourceSpan, amount, coverage, sourceSpanBuffer); + Assert.Equal(expected, destination); blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, source, amount, coverage, constantSourceBuffer); - Assert.All(destination, x => Assert.Equal(expected, x)); + Assert.Equal(expected, destination); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = BlendWithCoverageScalar(blender, background, source, amounts[i], coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, sourceSpan, amounts, coverage, sourceSpanBuffer); + Assert.Equal(expected, destination); + + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, source, amounts, coverage, constantSourceBuffer); + Assert.Equal(expected, destination); + } + + /// + /// Computes a one-pixel coverage result through the scalar remainder path. + /// + /// The pixel format. + /// The blender under test. + /// The background pixel. + /// The source pixel. + /// The source opacity. + /// The pixel coverage. + /// The blended pixel. + private static TPixel BlendWithCoverageScalar( + PixelBlender blender, + TPixel background, + TPixel source, + float amount, + float coverage) + where TPixel : unmanaged, IPixel + { + Span destination = stackalloc TPixel[1]; + Span backgroundSpan = stackalloc TPixel[1] { background }; + Span sourceSpan = stackalloc TPixel[1] { source }; + Span coverageSpan = stackalloc float[1] { coverage }; + Span buffer = stackalloc Vector4[3]; + + blender.BlendWithCoverage( + Configuration.Default, + destination, + backgroundSpan, + sourceSpan, + amount, + coverageSpan, + buffer); + + return destination[0]; } }