From c4683902a4aa1187e3cd2176c15306a139e2da39 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 15 Jul 2026 07:47:16 +1000 Subject: [PATCH] Add associated-alpha pixel formats Introduce Rgba32P, Bgra32P, Argb32P, Abgr32P, NormalizedByte4P, and HalfVector4P. Add representation-aware scalar and bulk conversions, optimized pixel operations, and associated-alpha blending across all Porter-Duff modes. Include comprehensive conversion, layout, blending, and performance coverage. --- src/ImageSharp/Advanced/AotCompilerTools.cs | 12 + src/ImageSharp/Color/Color.cs | 139 +- .../Common/Helpers/SimdUtils.Convert.cs | 32 +- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 60 +- .../Common/Helpers/Vector128Utilities.cs | 25 + .../Common/Helpers/Vector256Utilities.cs | 20 + .../Common/Helpers/Vector512Utilities.cs | 13 + src/ImageSharp/ImageSharp.csproj | 18 + .../AssociatedAlphaPixelOperations{TPixel}.cs | 258 + .../AssociatedAlphaPixelBlenders.Generated.cs | 84152 ++++++++++++++++ .../AssociatedAlphaPixelBlenders.Generated.tt | 847 + .../AssociatedAlphaPixelBlenders.cs | 168 + .../AssociatedAlphaPixelBlender{TPixel}.cs | 46 + ...iatedAlphaPorterDuffFunctions.Generated.cs | 5035 + ...iatedAlphaPorterDuffFunctions.Generated.tt | 138 + .../AssociatedAlphaPorterDuffFunctions.cs | 546 + .../PixelBlenders/PorterDuffFunctions.cs | 4 +- .../PixelFormats/PixelBlender{TPixel}.cs | 82 +- .../PixelImplementations/Abgr32P.cs | 241 + .../PixelImplementations/Argb32P.cs | 241 + .../PixelImplementations/Bgra32P.cs | 241 + .../PixelImplementations/HalfVector4P.cs | 243 + .../PixelImplementations/NormalizedByte4P.cs | 276 + .../Abgr32P.PixelOperations.cs | 66 + .../Argb32P.PixelOperations.cs | 66 + .../Bgra32P.PixelOperations.cs | 66 + .../Bgr24.PixelOperations.Generated.cs | 3 +- .../Rgb24.PixelOperations.Generated.cs | 3 +- .../Generated/_Common.ttinclude | 14 +- .../HalfVector4P.PixelOperations.cs | 57 + .../NormalizedByte4P.PixelOperations.cs | 62 + .../Rgba32P.PixelOperations.cs | 66 + .../RgbaVector.PixelOperations.cs | 5 +- .../PixelImplementations/Rgba32P.cs | 241 + .../PixelFormats/PixelOperations{TPixel}.cs | 65 +- ...tor4Converters.AssociatedRgbaCompatible.cs | 595 + .../Utils/Vector4Converters.RgbaCompatible.cs | 11 +- .../ImageSharp.Benchmarks/Bulk/FromVector4.cs | 79 +- .../Bulk/ToVector4_Bgra32.cs | 64 +- tests/ImageSharp.Benchmarks/Config.cs | 29 + .../AssociatedAlphaPixelBlenderBenchmark.cs | 101 + .../Color/ColorTests.CastFrom.cs | 14 + .../Color/ColorTests.CastTo.cs | 59 + tests/ImageSharp.Tests/Color/ColorTests.cs | 22 + .../ImageSharp.Tests/Common/SimdUtilsTests.cs | 43 +- .../PixelFormats/AssociatedAlphaPixelTests.cs | 667 + .../PixelFormats/PixelBlenderTests.cs | 173 +- ...ConverterTests.ReferenceImplementations.cs | 8 +- ...elOperationsTests.Specialized.Generated.cs | 93 +- .../Generated/_Common.ttinclude | 16 +- .../PixelOperations/PixelOperationsTests.cs | 135 +- ...GifDecoder_Decode_Resize_giphy_150_150.png | 4 +- ...ngDecoder_Decode_Resize_splash_150_150.png | 4 +- ...der_Decode_Resize_rgb_a_rle_UL_150_150.png | 4 +- ..._JpegCompressedWithIssue2679_Issue2679.png | 4 +- ...size_RgbaUnassociatedAlpha3bit_150_150.png | 4 +- ...er_Decode_Resize_bike_lossless_150_150.png | 4 +- 57 files changed, 95465 insertions(+), 219 deletions(-) create mode 100644 src/ImageSharp/PixelFormats/AssociatedAlphaPixelOperations{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.tt create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.cs create mode 100644 src/ImageSharp/PixelFormats/PixelImplementations/Abgr32P.cs create mode 100644 src/ImageSharp/PixelFormats/PixelImplementations/Argb32P.cs create mode 100644 src/ImageSharp/PixelFormats/PixelImplementations/Bgra32P.cs create mode 100644 src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4P.cs create mode 100644 src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4P.cs create mode 100644 src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32P.PixelOperations.cs create mode 100644 src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32P.PixelOperations.cs create mode 100644 src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32P.PixelOperations.cs create mode 100644 src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4P.PixelOperations.cs create mode 100644 src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4P.PixelOperations.cs create mode 100644 src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32P.PixelOperations.cs create mode 100644 src/ImageSharp/PixelFormats/PixelImplementations/Rgba32P.cs create mode 100644 src/ImageSharp/PixelFormats/Utils/Vector4Converters.AssociatedRgbaCompatible.cs create mode 100644 tests/ImageSharp.Benchmarks/PixelBlenders/AssociatedAlphaPixelBlenderBenchmark.cs create mode 100644 tests/ImageSharp.Tests/PixelFormats/AssociatedAlphaPixelTests.cs diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 0f28b28901..ce41e3a0d6 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -81,10 +81,13 @@ internal static class AotCompilerTools Seed(); Seed(); + Seed(); Seed(); + Seed(); Seed(); Seed(); Seed(); + Seed(); Seed(); Seed(); Seed(); @@ -95,8 +98,10 @@ internal static class AotCompilerTools Seed(); Seed(); Seed(); + Seed(); Seed(); Seed(); + Seed(); Seed(); Seed(); Seed(); @@ -104,6 +109,7 @@ internal static class AotCompilerTools Seed(); Seed(); Seed(); + Seed(); Seed(); Seed(); Seed(); @@ -158,10 +164,13 @@ internal static class AotCompilerTools Image img = default; img.CloneAs(default); img.CloneAs(default); + img.CloneAs(default); img.CloneAs(default); + img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); + img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); @@ -172,8 +181,10 @@ internal static class AotCompilerTools img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); + img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); + img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); @@ -181,6 +192,7 @@ internal static class AotCompilerTools img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); + img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index dd248d488f..7969f4728d 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -20,30 +20,67 @@ namespace SixLabors.ImageSharp; public readonly partial struct Color : IEquatable { private readonly Vector4 data; + private readonly Vector4 unassociatedData; private readonly IPixel? boxedHighPrecisionPixel; + private readonly bool isAssociated; /// /// Initializes a new instance of the struct. /// /// The containing the color information. + /// The alpha representation of . [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Color(Vector4 vector) + private Color(Vector4 vector, PixelAlphaRepresentation alphaRepresentation) + { + Vector4 clamped = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); + Vector4 unassociated = clamped; + + if (alphaRepresentation == PixelAlphaRepresentation.Associated) + { + Numerics.UnPremultiply(ref unassociated); + } + + this.data = clamped; + this.unassociatedData = unassociated; + this.boxedHighPrecisionPixel = null; + this.isAssociated = alphaRepresentation == PixelAlphaRepresentation.Associated; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The containing the color information. + /// The unassociated representation of . + /// The alpha representation of . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Color(Vector4 vector, Vector4 unassociatedVector, PixelAlphaRepresentation alphaRepresentation) { this.data = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); + this.unassociatedData = Numerics.Clamp(unassociatedVector, Vector4.Zero, Vector4.One); this.boxedHighPrecisionPixel = null; + this.isAssociated = alphaRepresentation == PixelAlphaRepresentation.Associated; } /// /// Initializes a new instance of the struct. /// /// The pixel containing color information. + /// The alpha representation of . [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Color(IPixel pixel) + private Color(IPixel pixel, PixelAlphaRepresentation alphaRepresentation) { this.boxedHighPrecisionPixel = pixel; this.data = default; + this.unassociatedData = default; + this.isAssociated = alphaRepresentation == PixelAlphaRepresentation.Associated; } + /// + /// Gets the alpha representation used by this color's scaled vector. + /// + public PixelAlphaRepresentation AlphaRepresentation + => this.isAssociated ? PixelAlphaRepresentation.Associated : PixelAlphaRepresentation.Unassociated; + /// /// Checks whether two structures are equal. /// @@ -80,21 +117,36 @@ public readonly partial struct Color : IEquatable { // Avoid boxing in case we can convert to Vector4 safely and efficiently PixelTypeInfo info = TPixel.GetPixelTypeInfo(); + if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentBitDepth.Bit32) { - return new Color(source.ToScaledVector4()); + Vector4 vector = source.ToScaledVector4(); + Vector4 unassociated = info.AlphaRepresentation == PixelAlphaRepresentation.Associated + ? PixelOperations.Instance.ToUnassociatedScaledVector4(source) + : vector; + + return new Color(vector, unassociated, info.AlphaRepresentation); } - return new Color(source); + return new Color(source, info.AlphaRepresentation); } /// /// Creates a from a generic scaled . /// - /// The vector to load the pixel from. + /// The unassociated vector to load the color from. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Color FromScaledVector(Vector4 source) => new(source, PixelAlphaRepresentation.Unassociated); + + /// + /// Creates a from a generic scaled with the specified alpha representation. + /// + /// The vector to load the color from. + /// The alpha representation of . /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Color FromScaledVector(Vector4 source) => new(source); + public static Color FromScaledVector(Vector4 source, PixelAlphaRepresentation alphaRepresentation) => new(source, alphaRepresentation); /// /// Bulk converts a span of generic scaled to a span of . @@ -103,11 +155,23 @@ public readonly partial struct Color : IEquatable /// The destination color span. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FromScaledVector(ReadOnlySpan source, Span destination) + => FromScaledVector(source, destination, PixelAlphaRepresentation.Unassociated); + + /// + /// Bulk converts a span of generic scaled values with the specified alpha representation + /// to a span of values. + /// + /// The source vector span. + /// The destination color span. + /// The alpha representation of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void FromScaledVector(ReadOnlySpan source, Span destination, PixelAlphaRepresentation alphaRepresentation) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) { - destination[i] = FromScaledVector(source[i]); + destination[i] = FromScaledVector(source[i], alphaRepresentation); } } @@ -125,18 +189,23 @@ public readonly partial struct Color : IEquatable // Avoid boxing in case we can convert to Vector4 safely and efficiently PixelTypeInfo info = TPixel.GetPixelTypeInfo(); + if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentBitDepth.Bit32) { + bool isAssociated = info.AlphaRepresentation == PixelAlphaRepresentation.Associated; + for (int i = 0; i < source.Length; i++) { - destination[i] = FromScaledVector(source[i].ToScaledVector4()); + Vector4 vector = source[i].ToScaledVector4(); + Vector4 unassociated = isAssociated ? PixelOperations.Instance.ToUnassociatedScaledVector4(source[i]) : vector; + destination[i] = new Color(vector, unassociated, info.AlphaRepresentation); } } else { for (int i = 0; i < source.Length; i++) { - destination[i] = new Color(source[i]); + destination[i] = new Color(source[i], info.AlphaRepresentation); } } } @@ -276,13 +345,30 @@ public readonly partial struct Color : IEquatable /// Alters the alpha channel of the color, returning a new instance. /// /// The new value of alpha [0..1]. - /// The color having it's alpha channel altered. + /// The color having its alpha channel altered. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color WithAlpha(float alpha) { Vector4 v = this.ToScaledVector4(); - v.W = alpha; - return FromScaledVector(v); + float clampedAlpha = Numerics.Clamp(alpha, 0, 1); + + if (this.isAssociated) + { + Vector4 unassociated = this.boxedHighPrecisionPixel is null ? this.unassociatedData : v; + + if (this.boxedHighPrecisionPixel is not null) + { + Numerics.UnPremultiply(ref unassociated); + } + + unassociated.W = clampedAlpha; + Vector4 associated = unassociated; + Numerics.Premultiply(ref associated); + return new Color(associated, unassociated, PixelAlphaRepresentation.Associated); + } + + v.W = clampedAlpha; + return FromScaledVector(v, PixelAlphaRepresentation.Unassociated); } /// @@ -296,9 +382,7 @@ public readonly partial struct Color : IEquatable [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToHex(ColorHexFormat format = ColorHexFormat.Rgba) { - Rgba32 rgba = (this.boxedHighPrecisionPixel is not null) - ? this.boxedHighPrecisionPixel.ToRgba32() - : Rgba32.FromScaledVector4(this.data); + Rgba32 rgba = this.ToPixel(); uint hexOrder = format switch { @@ -327,17 +411,23 @@ public readonly partial struct Color : IEquatable return pixel; } - if (this.boxedHighPrecisionPixel is null) + Vector4 vector = this.boxedHighPrecisionPixel is null + ? this.isAssociated ? this.unassociatedData : this.data + : this.boxedHighPrecisionPixel.ToScaledVector4(); + + if (this.isAssociated && this.boxedHighPrecisionPixel is not null) { - return TPixel.FromScaledVector4(this.data); + Numerics.UnPremultiply(ref vector); } - return TPixel.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4()); + // Destination pixel operations own association because RGB must use the alpha value representable by that destination format. + return PixelOperations.Instance.FromUnassociatedScaledVector4(vector); } /// - /// Expands the color into a generic ("scaled") representation - /// with values scaled and clamped between 0 and 1. + /// Expands the color into a generic ("scaled") representation, + /// preserving the , with values scaled and clamped between + /// 0 and 1. /// The vector components are typically expanded in least to greatest significance order. /// /// The . @@ -377,10 +467,11 @@ public readonly partial struct Color : IEquatable { if (this.boxedHighPrecisionPixel is null && other.boxedHighPrecisionPixel is null) { - return this.data == other.data; + return this.data == other.data && this.isAssociated == other.isAssociated; } - return this.boxedHighPrecisionPixel?.Equals(other.boxedHighPrecisionPixel) == true; + return this.isAssociated == other.isAssociated + && this.boxedHighPrecisionPixel?.Equals(other.boxedHighPrecisionPixel) == true; } /// @@ -392,10 +483,10 @@ public readonly partial struct Color : IEquatable { if (this.boxedHighPrecisionPixel is null) { - return this.data.GetHashCode(); + return HashCode.Combine(this.data, this.isAssociated); } - return this.boxedHighPrecisionPixel.GetHashCode(); + return HashCode.Combine(this.boxedHighPrecisionPixel.ToScaledVector4(), this.isAssociated); } /// diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs index 5318ad0497..2efa3f5450 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs @@ -41,38 +41,58 @@ internal static partial class SimdUtils { DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); - HwIntrinsics.NormalizedFloatToByteSaturateReduce(ref source, ref destination); + HwIntrinsics.FloatToByteSaturateReduce(ref source, ref destination, byte.MaxValue); if (source.Length > 0) { - ConvertNormalizedFloatToByteRemainder(source, destination); + ConvertFloatToByteRemainder(source, destination, byte.MaxValue); + } + } + + /// + /// Converts byte-magnitude floating-point values to bytes using saturating round-to-nearest with midpoint values away from zero. + /// + /// The source byte magnitudes. + /// The destination bytes. + [MethodImpl(InliningOptions.ShortMethod)] + internal static void FloatToByteSaturate(ReadOnlySpan source, Span destination) + { + DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); + + HwIntrinsics.FloatToByteSaturateReduce(ref source, ref destination, 1F); + + if (source.Length > 0) + { + ConvertFloatToByteRemainder(source, destination, 1F); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void ConvertByteToNormalizedFloatRemainder(ReadOnlySpan source, Span destination) { + const float scale = 1F / byte.MaxValue; ref byte sBase = ref MemoryMarshal.GetReference(source); ref float dBase = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < source.Length; i++) { - Unsafe.Add(ref dBase, (uint)i) = Unsafe.Add(ref sBase, (uint)i) / 255f; + // Match the SIMD conversion so one span cannot contain different float representations of the same byte value. + Unsafe.Add(ref dBase, (uint)i) = Unsafe.Add(ref sBase, (uint)i) * scale; } } [MethodImpl(MethodImplOptions.NoInlining)] - private static void ConvertNormalizedFloatToByteRemainder(ReadOnlySpan source, Span destination) + private static void ConvertFloatToByteRemainder(ReadOnlySpan source, Span destination, float scale) { ref float sBase = ref MemoryMarshal.GetReference(source); ref byte dBase = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < source.Length; i++) { - Unsafe.Add(ref dBase, (uint)i) = ConvertToByte(Unsafe.Add(ref sBase, (uint)i)); + Unsafe.Add(ref dBase, (uint)i) = ConvertToByte(Unsafe.Add(ref sBase, (uint)i), scale); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte ConvertToByte(float f) => (byte)Numerics.Clamp((f * 255f) + 0.5f, 0, 255f); + private static byte ConvertToByte(float value, float scale) => (byte)Numerics.Clamp((value * scale) + .5F, 0, byte.MaxValue); } diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 022056deb0..cb172d145e 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -834,6 +834,19 @@ internal static partial class SimdUtils internal static void NormalizedFloatToByteSaturateReduce( ref ReadOnlySpan source, ref Span destination) + => FloatToByteSaturateReduce(ref source, ref destination, byte.MaxValue); + + /// + /// Converts as many scaled floating-point values as possible to bytes and retains the unconverted remainder. + /// + /// The source buffer. + /// The destination buffer. + /// The factor applied before conversion. + [MethodImpl(InliningOptions.ShortMethod)] + internal static void FloatToByteSaturateReduce( + ref ReadOnlySpan source, + ref Span destination, + float scaleFactor) { DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); @@ -858,9 +871,10 @@ internal static partial class SimdUtils if (adjustedCount > 0) { - NormalizedFloatToByteSaturate( + FloatToByteSaturate( source[..adjustedCount], - destination[..adjustedCount]); + destination[..adjustedCount], + scaleFactor); source = source[adjustedCount..]; destination = destination[adjustedCount..]; @@ -880,6 +894,18 @@ internal static partial class SimdUtils internal static void NormalizedFloatToByteSaturate( ReadOnlySpan source, Span destination) + => FloatToByteSaturate(source, destination, byte.MaxValue); + + /// + /// Converts scaled floating-point values to bytes using saturating round-to-nearest with midpoint values away from zero. + /// + /// The source buffer. + /// The destination buffer. + /// The factor applied before conversion. + internal static void FloatToByteSaturate( + ReadOnlySpan source, + Span destination, + float scaleFactor) { if (Vector512.IsHardwareAccelerated && Avx512BW.IsSupported) { @@ -890,7 +916,7 @@ internal static partial class SimdUtils ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - Vector512 scale = Vector512.Create((float)byte.MaxValue); + Vector512 scale = Vector512.Create(scaleFactor); Vector512 mask = PermuteMaskDeinterleave16x32(); for (nuint i = 0; i < n; i++) @@ -902,10 +928,10 @@ internal static partial class SimdUtils Vector512 f2 = scale * Unsafe.Add(ref s, 2); Vector512 f3 = scale * Unsafe.Add(ref s, 3); - Vector512 w0 = Vector512_.ConvertToInt32RoundToEven(f0); - Vector512 w1 = Vector512_.ConvertToInt32RoundToEven(f1); - Vector512 w2 = Vector512_.ConvertToInt32RoundToEven(f2); - Vector512 w3 = Vector512_.ConvertToInt32RoundToEven(f3); + Vector512 w0 = Vector512_.ConvertToInt32RoundAwayFromZero(f0); + Vector512 w1 = Vector512_.ConvertToInt32RoundAwayFromZero(f1); + Vector512 w2 = Vector512_.ConvertToInt32RoundAwayFromZero(f2); + Vector512 w3 = Vector512_.ConvertToInt32RoundAwayFromZero(f3); Vector512 u0 = Avx512BW.PackSignedSaturate(w0, w1); Vector512 u1 = Avx512BW.PackSignedSaturate(w2, w3); @@ -924,7 +950,7 @@ internal static partial class SimdUtils ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - Vector256 scale = Vector256.Create((float)byte.MaxValue); + Vector256 scale = Vector256.Create(scaleFactor); Vector256 mask = PermuteMaskDeinterleave8x32(); for (nuint i = 0; i < n; i++) @@ -936,10 +962,10 @@ internal static partial class SimdUtils Vector256 f2 = scale * Unsafe.Add(ref s, 2); Vector256 f3 = scale * Unsafe.Add(ref s, 3); - Vector256 w0 = Vector256_.ConvertToInt32RoundToEven(f0); - Vector256 w1 = Vector256_.ConvertToInt32RoundToEven(f1); - Vector256 w2 = Vector256_.ConvertToInt32RoundToEven(f2); - Vector256 w3 = Vector256_.ConvertToInt32RoundToEven(f3); + Vector256 w0 = Vector256_.ConvertToInt32RoundAwayFromZero(f0); + Vector256 w1 = Vector256_.ConvertToInt32RoundAwayFromZero(f1); + Vector256 w2 = Vector256_.ConvertToInt32RoundAwayFromZero(f2); + Vector256 w3 = Vector256_.ConvertToInt32RoundAwayFromZero(f3); Vector256 u0 = Avx2.PackSignedSaturate(w0, w1); Vector256 u1 = Avx2.PackSignedSaturate(w2, w3); @@ -959,7 +985,7 @@ internal static partial class SimdUtils ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector128 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - Vector128 scale = Vector128.Create((float)byte.MaxValue); + Vector128 scale = Vector128.Create(scaleFactor); Vector128 min = Vector128.Zero; Vector128 max = Vector128.Create((int)byte.MaxValue); @@ -972,10 +998,10 @@ internal static partial class SimdUtils Vector128 f2 = scale * Unsafe.Add(ref s, 2); Vector128 f3 = scale * Unsafe.Add(ref s, 3); - Vector128 w0 = Vector128_.ConvertToInt32RoundToEven(f0); - Vector128 w1 = Vector128_.ConvertToInt32RoundToEven(f1); - Vector128 w2 = Vector128_.ConvertToInt32RoundToEven(f2); - Vector128 w3 = Vector128_.ConvertToInt32RoundToEven(f3); + Vector128 w0 = Vector128_.ConvertToInt32RoundAwayFromZero(f0); + Vector128 w1 = Vector128_.ConvertToInt32RoundAwayFromZero(f1); + Vector128 w2 = Vector128_.ConvertToInt32RoundAwayFromZero(f2); + Vector128 w3 = Vector128_.ConvertToInt32RoundAwayFromZero(f3); w0 = Vector128_.Clamp(w0, min, max); w1 = Vector128_.Clamp(w1, min, max); diff --git a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs index a5d377eb90..7ea77fadfb 100644 --- a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs @@ -307,6 +307,31 @@ internal static class Vector128_ return Vector128.ConvertToInt32(val_2p23_f32 | sign); } + /// + /// Converts all values in to signed 32-bit integers, rounding midpoint values away from zero. + /// + /// The values to convert. + /// The converted integer values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 ConvertToInt32RoundAwayFromZero(Vector128 vector) + { + if (Sse2.IsSupported) + { + // The x86 conversion truncates, so adding one half with each lane's sign implements round-to-nearest with midpoint values away from zero. + Vector128 x86Adjustment = Vector128.Create(.5F) | (vector & Vector128.Create(-0F)); + return Sse2.ConvertToVector128Int32WithTruncation(vector + x86Adjustment); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.ConvertToInt32RoundAwayFromZero(vector); + } + + Vector128 sign = vector & Vector128.Create(-0F); + Vector128 fallbackAdjustment = Vector128.Create(.5F) | sign; + return Vector128.ConvertToInt32(vector + fallbackAdjustment); + } + /// /// Rounds all values in to the nearest integer /// following semantics. diff --git a/src/ImageSharp/Common/Helpers/Vector256Utilities.cs b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs index 90e3169b37..caef699dd1 100644 --- a/src/ImageSharp/Common/Helpers/Vector256Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs @@ -73,6 +73,26 @@ internal static class Vector256_ return Vector256.ConvertToInt32(val_2p23_f32 | sign); } + /// + /// Converts all values in to signed 32-bit integers, rounding midpoint values away from zero. + /// + /// The values to convert. + /// The converted integer values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ConvertToInt32RoundAwayFromZero(Vector256 vector) + { + if (Avx.IsSupported) + { + // The x86 conversion truncates, so adding one half with each lane's sign implements round-to-nearest with midpoint values away from zero. + Vector256 x86Adjustment = Vector256.Create(.5F) | (vector & Vector256.Create(-0F)); + return Avx.ConvertToVector256Int32WithTruncation(vector + x86Adjustment); + } + + Vector256 sign = vector & Vector256.Create(-0F); + Vector256 fallbackAdjustment = Vector256.Create(.5F) | sign; + return Vector256.ConvertToInt32(vector + fallbackAdjustment); + } + /// /// Rounds all values in to the nearest integer /// following semantics. diff --git a/src/ImageSharp/Common/Helpers/Vector512Utilities.cs b/src/ImageSharp/Common/Helpers/Vector512Utilities.cs index 82a20158ae..17f9e9c51e 100644 --- a/src/ImageSharp/Common/Helpers/Vector512Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector512Utilities.cs @@ -59,6 +59,19 @@ internal static class Vector512_ public static Vector512 ConvertToInt32RoundToEven(Vector512 vector) => Avx512F.ConvertToVector512Int32(vector); + /// + /// Converts all values in to signed 32-bit integers, rounding midpoint values away from zero. + /// + /// The values to convert. + /// The converted integer values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ConvertToInt32RoundAwayFromZero(Vector512 vector) + { + // The x86 conversion truncates, so adding one half with each lane's sign implements round-to-nearest with midpoint values away from zero. + Vector512 half = Vector512.Create(.5F) | (vector & Vector512.Create(-0F)); + return Avx512F.ConvertToVector512Int32WithTruncation(vector + half); + } + /// /// Rounds all values in to the nearest integer /// following semantics. diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 32a5f0073e..9f545fc2a2 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -141,6 +141,16 @@ True DefaultPixelBlenders.Generated.tt + + True + True + AssociatedAlphaPixelBlenders.Generated.tt + + + True + True + AssociatedAlphaPorterDuffFunctions.Generated.tt + True True @@ -221,6 +231,14 @@ DefaultPixelBlenders.Generated.cs TextTemplatingFileGenerator + + AssociatedAlphaPixelBlenders.Generated.cs + TextTemplatingFileGenerator + + + AssociatedAlphaPorterDuffFunctions.Generated.cs + TextTemplatingFileGenerator + TextTemplatingFileGenerator ImageExtensions.Save.cs diff --git a/src/ImageSharp/PixelFormats/AssociatedAlphaPixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/AssociatedAlphaPixelOperations{TPixel}.cs new file mode 100644 index 0000000000..2c84bcfc80 --- /dev/null +++ b/src/ImageSharp/PixelFormats/AssociatedAlphaPixelOperations{TPixel}.cs @@ -0,0 +1,258 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides bulk operations for pixel formats that store associated alpha. +/// +/// The associated-alpha pixel format. +internal class AssociatedAlphaPixelOperations : PixelOperations + where TPixel : unmanaged, IPixel +{ + /// + public override PixelBlender GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) + => AssociatedAlphaPixelBlenders.GetPixelBlender(colorMode, alphaMode); + + /// + internal override Vector4 ToUnassociatedScaledVector4(TPixel source) + { + Vector4 vector = source.ToScaledVector4(); + Numerics.UnPremultiply(ref vector); + return vector; + } + + /// + internal override TPixel FromUnassociatedScaledVector4(Vector4 source) => TPixel.FromScaledVector4(Associate(source)); + + /// + /// Converts an associated scaled vector to a destination pixel after associating RGB with the alpha value the destination stores. + /// + /// The associated scaled vector. + /// The destination pixel. + public virtual TPixel FromAssociatedScaledVector4(Vector4 source) => TPixel.FromScaledVector4(Reassociate(source)); + + /// + internal override void ToUnassociatedScaledVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination) + { + this.ToVector4(configuration, source, destination, PixelConversionModifiers.Scale); + Numerics.UnPremultiply(destination[..source.Length]); + } + + /// + internal override void ToAssociatedScaledVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination) + => this.ToVector4(configuration, source, destination, PixelConversionModifiers.Scale); + + /// + internal override void FromUnassociatedScaledVector4( + Configuration configuration, + Span source, + Span destination) + { + source = source[..destination.Length]; + + for (int i = 0; i < source.Length; i++) + { + source[i] = Associate(source[i]); + } + + this.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale); + } + + /// + /// Converts associated scaled vectors to destination pixels after associating RGB with the alpha values the destination stores. + /// + /// The configuration. + /// The associated scaled vectors. + /// The destination pixels. + public virtual void FromAssociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + + for (int i = 0; i < source.Length; i++) + { + source[i] = Reassociate(source[i]); + } + + this.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale); + } + + /// + public override void From( + Configuration configuration, + ReadOnlySpan source, + Span destination) + { + const int sliceLength = 1024; + int numberOfSlices = source.Length / sliceLength; + + using IMemoryOwner tempVectors = configuration.MemoryAllocator.Allocate(sliceLength); + Span vectorSpan = tempVectors.GetSpan(); + + // Convert through unassociated vectors so the destination operation can quantize alpha to its own storage before associating RGB. + for (int i = 0; i < numberOfSlices; i++) + { + int start = i * sliceLength; + ReadOnlySpan sourceSlice = source.Slice(start, sliceLength); + Span destinationSlice = destination.Slice(start, sliceLength); + PixelOperations.Instance.ToUnassociatedScaledVector4(configuration, sourceSlice, vectorSpan); + this.FromUnassociatedScaledVector4(configuration, vectorSpan, destinationSlice); + } + + int endOfCompleteSlices = numberOfSlices * sliceLength; + int remainder = source.Length - endOfCompleteSlices; + + if (remainder > 0) + { + ReadOnlySpan sourceSlice = source[endOfCompleteSlices..]; + Span destinationSlice = destination[endOfCompleteSlices..]; + vectorSpan = vectorSpan[..remainder]; + PixelOperations.Instance.ToUnassociatedScaledVector4(configuration, sourceSlice, vectorSpan); + this.FromUnassociatedScaledVector4(configuration, vectorSpan, destinationSlice); + } + } + + /// + public override void FromVector4Destructive( + Configuration configuration, + Span sourceVectors, + Span destination, + PixelConversionModifiers modifiers) + => base.FromVector4Destructive(configuration, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Premultiply)); + + /// + public override void ToVector4( + Configuration configuration, + ReadOnlySpan source, + Span destinationVectors, + PixelConversionModifiers modifiers) + => base.ToVector4(configuration, source, destinationVectors, modifiers.Remove(PixelConversionModifiers.Premultiply)); + + /// + public override void ToArgb32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToAbgr32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToBgr24(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToBgra32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToL8(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToL16(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToLa16(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToLa32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToRgb24(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToRgba32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToRgb48(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToRgba64(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToBgra5551(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + /// Converts associated source pixels to an unassociated destination format. + /// + /// The destination pixel format. + /// The configuration. + /// The source pixels. + /// The destination pixels. + private void ConvertToUnassociated( + Configuration configuration, + ReadOnlySpan source, + Span destination) + where TDestinationPixel : unmanaged, IPixel + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + ref TPixel sourceBase = ref MemoryMarshal.GetReference(source); + ref TDestinationPixel destinationBase = ref MemoryMarshal.GetReference(destination); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Vector4 vector = this.ToUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i)); + Unsafe.Add(ref destinationBase, i) = TDestinationPixel.FromScaledVector4(vector); + } + } + + /// + /// Converts an unassociated scaled vector to the associated representation of the destination pixel type. + /// + /// The unassociated scaled vector. + /// The associated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Associate(Vector4 source) + { + // Round-trip alpha through TPixel so the generic fallback associates RGB with the value the destination actually stores. + source.W = TPixel.FromScaledVector4(new Vector4(0, 0, 0, source.W)).ToScaledVector4().W; + Numerics.Premultiply(ref source); + return source; + } + + /// + /// Reassociates a scaled vector with the alpha value the destination pixel can store. + /// + /// The associated scaled vector. + /// The reassociated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Reassociate(Vector4 source) + { + float alpha = source.W; + + if (alpha == 0) + { + return Vector4.Zero; + } + + float storedAlpha = TPixel.FromScaledVector4(new Vector4(0, 0, 0, alpha)).ToScaledVector4().W; + + // Associated RGB scales by the same ratio as alpha. Applying that ratio directly avoids the extra division and multiplication of an unpremultiply/premultiply round trip and preserves exact midpoints when alpha needs no quantization. + source *= storedAlpha / alpha; + source.W = storedAlpha; + return source; + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs new file mode 100644 index 0000000000..49c1e47095 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs @@ -0,0 +1,84152 @@ +// 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 +{ + + /// + /// A pixel blender that implements the "NormalSrc" composition equation. + /// + public sealed class NormalSrc : AssociatedAlphaPixelBlender + { + /// + /// 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.NormalSrc(background.ToScaledVector4(), source.ToScaledVector4(), 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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); + } + } + } + + /// + 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.NormalSrc(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.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); + + 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.NormalSrc(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.NormalSrc(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = AssociatedAlphaPorterDuffFunctions.NormalSrc(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.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); + } + + 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); + + ref Vector256 backgroundBase = 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + ref Vector256 backgroundBase = 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + } + + /// + /// A pixel blender that implements the "MultiplySrc" composition equation. + /// + public sealed class MultiplySrc : AssociatedAlphaPixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static MultiplySrc Instance { get; } = new MultiplySrc(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return FromBlendVector4(AssociatedAlphaPorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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); + } + } + } + + /// + 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.MultiplySrc(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.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); + + 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.MultiplySrc(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.MultiplySrc(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = AssociatedAlphaPorterDuffFunctions.MultiplySrc(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.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); + + ref Vector256 backgroundBase = 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + ref Vector256 backgroundBase = 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + } + + /// + /// A pixel blender that implements the "AddSrc" composition equation. + /// + public sealed class AddSrc : AssociatedAlphaPixelBlender + { + /// + /// 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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); + } + } + } + + /// + 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.AddSrc(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.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); + + 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.AddSrc(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.AddSrc(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = AssociatedAlphaPorterDuffFunctions.AddSrc(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.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); + } + + 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); + + ref Vector256 backgroundBase = 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + ref Vector256 backgroundBase = 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + } + + /// + /// A pixel blender that implements the "SubtractSrc" composition equation. + /// + public sealed class SubtractSrc : AssociatedAlphaPixelBlender + { + /// + /// 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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); + } + } + } + + /// + 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.SubtractSrc(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.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); + + 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.SubtractSrc(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.SubtractSrc(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = AssociatedAlphaPorterDuffFunctions.SubtractSrc(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.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); + } + + 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); + + ref Vector256 backgroundBase = 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + ref Vector256 backgroundBase = 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + } + + /// + /// A pixel blender that implements the "ScreenSrc" composition equation. + /// + public sealed class ScreenSrc : AssociatedAlphaPixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static ScreenSrc Instance { get; } = new ScreenSrc(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return FromBlendVector4(AssociatedAlphaPorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), 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.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); + } + + 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); + + 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.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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.DarkenSrc(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.LightenSrc(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.OverlaySrc(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.HardLightSrc(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.AddSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.NormalSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.AddSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.LightenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.NormalSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.AddSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.LightenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.NormalSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.AddSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.LightenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.NormalDest(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.MultiplyDest(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.AddDest(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.SubtractDest(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.ScreenDest(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.DarkenDest(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.LightenDest(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.OverlayDest(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.HardLightDest(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.NormalDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.AddDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.LightenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.NormalDestOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.AddDestOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.SubtractDestOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.ScreenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.DarkenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.LightenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.OverlayDestOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.HardLightDestOver(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.NormalDestIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.AddDestIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.SubtractDestIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.ScreenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.DarkenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.LightenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.OverlayDestIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.HardLightDestIn(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.NormalDestOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.AddDestOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.SubtractDestOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.ScreenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.DarkenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.LightenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.OverlayDestOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.HardLightDestOut(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.NormalClear(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.MultiplyClear(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.AddClear(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.SubtractClear(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.ScreenClear(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.DarkenClear(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.LightenClear(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.OverlayClear(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.HardLightClear(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.NormalXor(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.MultiplyXor(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.AddXor(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.SubtractXor(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.ScreenXor(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.DarkenXor(background.ToScaledVector4(), source.ToScaledVector4(), 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.LightenXor(background.ToScaledVector4(), source.ToScaledVector4(), 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)); + } + } + } + } + + /// + /// A pixel blender that implements the "OverlayXor" composition equation. + /// + public sealed class OverlayXor : AssociatedAlphaPixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static OverlayXor Instance { get; } = new OverlayXor(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return FromBlendVector4(AssociatedAlphaPorterDuffFunctions.OverlayXor(background.ToScaledVector4(), source.ToScaledVector4(), 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)); + } + } + } + } + + /// + /// A pixel blender that implements the "HardLightXor" composition equation. + /// + public sealed class HardLightXor : AssociatedAlphaPixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static HardLightXor Instance { get; } = new HardLightXor(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return FromBlendVector4(AssociatedAlphaPorterDuffFunctions.HardLightXor(background.ToScaledVector4(), source.ToScaledVector4(), 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); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + 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); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + ref Vector256 backgroundBase = 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + + /// + 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.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); + } + + 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); + + 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.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); + } + + if (Numerics.Modulo2(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)); + } + } + } + } + +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt new file mode 100644 index 0000000000..b18f94a488 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt @@ -0,0 +1,847 @@ +<# +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +// +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 []{ + "Src", + "SrcAtop", + "SrcOver", + "SrcIn", + "SrcOut", + "Dest", + "DestAtop", + "DestOver", + "DestIn", + "DestOut", + "Clear", + "Xor", +}; + +var blenders = new []{ + "Normal", + "Multiply", + "Add", + "Subtract", + "Screen", + "Darken", + "Lighten", + "Overlay", + "HardLight" +}; + + foreach(var composer in composers) { + foreach(var blender in blenders) { + + var blender_composer= $"{blender}{composer}"; +#> + /// + /// A pixel blender that implements the "<#= blender_composer#>" composition equation. + /// + public sealed class <#= blender_composer#> : AssociatedAlphaPixelBlender + { + /// + /// 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 FromBlendVector4(AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background.ToScaledVector4(), source.ToScaledVector4(), 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.<#=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); + } + } + } + + /// + 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); + + 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)); + } + } + } + } + +<# + } +} + +#> +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.cs b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.cs new file mode 100644 index 0000000000..6ce1235b4c --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.cs @@ -0,0 +1,168 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +/// +/// Provides pixel blenders for formats that store associated alpha. +/// +internal static partial class AssociatedAlphaPixelBlenders + where TPixel : unmanaged, IPixel +{ + /// + /// Gets the blender for the requested color blending and alpha composition modes. + /// + /// The color blending mode. + /// The alpha composition mode. + /// The pixel blender. + public static PixelBlender GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) + { + return alphaMode switch + { + PixelAlphaCompositionMode.Src => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplySrc.Instance, + PixelColorBlendingMode.Add => AddSrc.Instance, + PixelColorBlendingMode.Subtract => SubtractSrc.Instance, + PixelColorBlendingMode.Screen => ScreenSrc.Instance, + PixelColorBlendingMode.Darken => DarkenSrc.Instance, + PixelColorBlendingMode.Lighten => LightenSrc.Instance, + PixelColorBlendingMode.Overlay => OverlaySrc.Instance, + PixelColorBlendingMode.HardLight => HardLightSrc.Instance, + _ => NormalSrc.Instance, + }, + PixelAlphaCompositionMode.SrcAtop => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplySrcAtop.Instance, + PixelColorBlendingMode.Add => AddSrcAtop.Instance, + PixelColorBlendingMode.Subtract => SubtractSrcAtop.Instance, + PixelColorBlendingMode.Screen => ScreenSrcAtop.Instance, + PixelColorBlendingMode.Darken => DarkenSrcAtop.Instance, + PixelColorBlendingMode.Lighten => LightenSrcAtop.Instance, + PixelColorBlendingMode.Overlay => OverlaySrcAtop.Instance, + PixelColorBlendingMode.HardLight => HardLightSrcAtop.Instance, + _ => NormalSrcAtop.Instance, + }, + PixelAlphaCompositionMode.SrcIn => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplySrcIn.Instance, + PixelColorBlendingMode.Add => AddSrcIn.Instance, + PixelColorBlendingMode.Subtract => SubtractSrcIn.Instance, + PixelColorBlendingMode.Screen => ScreenSrcIn.Instance, + PixelColorBlendingMode.Darken => DarkenSrcIn.Instance, + PixelColorBlendingMode.Lighten => LightenSrcIn.Instance, + PixelColorBlendingMode.Overlay => OverlaySrcIn.Instance, + PixelColorBlendingMode.HardLight => HardLightSrcIn.Instance, + _ => NormalSrcIn.Instance, + }, + PixelAlphaCompositionMode.SrcOut => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplySrcOut.Instance, + PixelColorBlendingMode.Add => AddSrcOut.Instance, + PixelColorBlendingMode.Subtract => SubtractSrcOut.Instance, + PixelColorBlendingMode.Screen => ScreenSrcOut.Instance, + PixelColorBlendingMode.Darken => DarkenSrcOut.Instance, + PixelColorBlendingMode.Lighten => LightenSrcOut.Instance, + PixelColorBlendingMode.Overlay => OverlaySrcOut.Instance, + PixelColorBlendingMode.HardLight => HardLightSrcOut.Instance, + _ => NormalSrcOut.Instance, + }, + PixelAlphaCompositionMode.Dest => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyDest.Instance, + PixelColorBlendingMode.Add => AddDest.Instance, + PixelColorBlendingMode.Subtract => SubtractDest.Instance, + PixelColorBlendingMode.Screen => ScreenDest.Instance, + PixelColorBlendingMode.Darken => DarkenDest.Instance, + PixelColorBlendingMode.Lighten => LightenDest.Instance, + PixelColorBlendingMode.Overlay => OverlayDest.Instance, + PixelColorBlendingMode.HardLight => HardLightDest.Instance, + _ => NormalDest.Instance, + }, + PixelAlphaCompositionMode.DestAtop => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyDestAtop.Instance, + PixelColorBlendingMode.Add => AddDestAtop.Instance, + PixelColorBlendingMode.Subtract => SubtractDestAtop.Instance, + PixelColorBlendingMode.Screen => ScreenDestAtop.Instance, + PixelColorBlendingMode.Darken => DarkenDestAtop.Instance, + PixelColorBlendingMode.Lighten => LightenDestAtop.Instance, + PixelColorBlendingMode.Overlay => OverlayDestAtop.Instance, + PixelColorBlendingMode.HardLight => HardLightDestAtop.Instance, + _ => NormalDestAtop.Instance, + }, + PixelAlphaCompositionMode.DestOver => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyDestOver.Instance, + PixelColorBlendingMode.Add => AddDestOver.Instance, + PixelColorBlendingMode.Subtract => SubtractDestOver.Instance, + PixelColorBlendingMode.Screen => ScreenDestOver.Instance, + PixelColorBlendingMode.Darken => DarkenDestOver.Instance, + PixelColorBlendingMode.Lighten => LightenDestOver.Instance, + PixelColorBlendingMode.Overlay => OverlayDestOver.Instance, + PixelColorBlendingMode.HardLight => HardLightDestOver.Instance, + _ => NormalDestOver.Instance, + }, + PixelAlphaCompositionMode.DestIn => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyDestIn.Instance, + PixelColorBlendingMode.Add => AddDestIn.Instance, + PixelColorBlendingMode.Subtract => SubtractDestIn.Instance, + PixelColorBlendingMode.Screen => ScreenDestIn.Instance, + PixelColorBlendingMode.Darken => DarkenDestIn.Instance, + PixelColorBlendingMode.Lighten => LightenDestIn.Instance, + PixelColorBlendingMode.Overlay => OverlayDestIn.Instance, + PixelColorBlendingMode.HardLight => HardLightDestIn.Instance, + _ => NormalDestIn.Instance, + }, + PixelAlphaCompositionMode.DestOut => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyDestOut.Instance, + PixelColorBlendingMode.Add => AddDestOut.Instance, + PixelColorBlendingMode.Subtract => SubtractDestOut.Instance, + PixelColorBlendingMode.Screen => ScreenDestOut.Instance, + PixelColorBlendingMode.Darken => DarkenDestOut.Instance, + PixelColorBlendingMode.Lighten => LightenDestOut.Instance, + PixelColorBlendingMode.Overlay => OverlayDestOut.Instance, + PixelColorBlendingMode.HardLight => HardLightDestOut.Instance, + _ => NormalDestOut.Instance, + }, + PixelAlphaCompositionMode.Clear => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyClear.Instance, + PixelColorBlendingMode.Add => AddClear.Instance, + PixelColorBlendingMode.Subtract => SubtractClear.Instance, + PixelColorBlendingMode.Screen => ScreenClear.Instance, + PixelColorBlendingMode.Darken => DarkenClear.Instance, + PixelColorBlendingMode.Lighten => LightenClear.Instance, + PixelColorBlendingMode.Overlay => OverlayClear.Instance, + PixelColorBlendingMode.HardLight => HardLightClear.Instance, + _ => NormalClear.Instance, + }, + PixelAlphaCompositionMode.Xor => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyXor.Instance, + PixelColorBlendingMode.Add => AddXor.Instance, + PixelColorBlendingMode.Subtract => SubtractXor.Instance, + PixelColorBlendingMode.Screen => ScreenXor.Instance, + PixelColorBlendingMode.Darken => DarkenXor.Instance, + PixelColorBlendingMode.Lighten => LightenXor.Instance, + PixelColorBlendingMode.Overlay => OverlayXor.Instance, + PixelColorBlendingMode.HardLight => HardLightXor.Instance, + _ => NormalXor.Instance, + }, + _ => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplySrcOver.Instance, + PixelColorBlendingMode.Add => AddSrcOver.Instance, + PixelColorBlendingMode.Subtract => SubtractSrcOver.Instance, + PixelColorBlendingMode.Screen => ScreenSrcOver.Instance, + PixelColorBlendingMode.Darken => DarkenSrcOver.Instance, + PixelColorBlendingMode.Lighten => LightenSrcOver.Instance, + PixelColorBlendingMode.Overlay => OverlaySrcOver.Instance, + PixelColorBlendingMode.HardLight => HardLightSrcOver.Instance, + _ => NormalSrcOver.Instance, + }, + }; + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlender{TPixel}.cs new file mode 100644 index 0000000000..64d0be6964 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlender{TPixel}.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +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 + where TPixel : unmanaged, IPixel +{ + // Associated blenders are created only by AssociatedAlphaPixelOperations, so the cached cast establishes the format-specific output boundary once per closed pixel type. + private static readonly AssociatedAlphaPixelOperations Operations = (AssociatedAlphaPixelOperations)PixelOperations.Instance; + + /// + protected override void ToBlendVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination) + { + // Selecting the source representation once per row avoids a format check for every blended pixel. + PixelOperations.Instance.ToAssociatedScaledVector4(configuration, source, destination); + } + + /// + protected override Vector4 ToBlendVector4(TPixel source) => source.ToScaledVector4(); + + /// + /// Converts an associated blend result to the destination pixel representation. + /// + /// The associated blend result. + /// The destination pixel. + public static TPixel FromBlendVector4(Vector4 source) => Operations.FromAssociatedScaledVector4(source); + + /// + protected override void FromBlendVector4( + Configuration configuration, + Span source, + Span destination) + { + Operations.FromAssociatedScaledVector4(configuration, source, destination); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.cs new file mode 100644 index 0000000000..cd5a37a117 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.cs @@ -0,0 +1,5035 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +// +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +internal static partial class AssociatedAlphaPorterDuffFunctions +{ + /// + /// Returns the associated-alpha result of the "NormalSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "NormalSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "NormalSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "MultiplySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplySrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "MultiplySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "MultiplySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "AddSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "AddSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "AddSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "SubtractSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "SubtractSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "SubtractSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ScreenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ScreenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ScreenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "DarkenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "DarkenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "DarkenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "LightenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "LightenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "LightenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "OverlaySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlaySrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "OverlaySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "OverlaySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "HardLightSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "HardLightSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "HardLightSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "NormalSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return AtopNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return AtopNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return AtopNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplySrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlaySrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return OverNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return OverNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return OverNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplySrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlaySrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplySrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlaySrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplySrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlaySrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "NormalDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "NormalDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "MultiplyDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "MultiplyDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "MultiplyDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "AddDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "AddDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "AddDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "SubtractDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "SubtractDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "SubtractDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ScreenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ScreenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ScreenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "DarkenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "DarkenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "DarkenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "LightenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "LightenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "LightenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "OverlayDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "OverlayDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "OverlayDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "HardLightDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "HardLightDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "HardLightDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "NormalDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return AtopNormal(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return AtopNormal(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return AtopNormal(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "AddDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "AddDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "AddDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LightenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LightenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LightenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "NormalDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return OverNormal(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return OverNormal(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return OverNormal(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "AddDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "AddDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "AddDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LightenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LightenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LightenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "NormalDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "AddDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "AddDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "AddDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LightenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LightenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LightenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "AddDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "AddDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "AddDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LightenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LightenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LightenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "NormalClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "NormalClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "MultiplyClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "MultiplyClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "MultiplyClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "AddClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "AddClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "AddClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "SubtractClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "SubtractClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "SubtractClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "ScreenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "ScreenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "ScreenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "DarkenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "DarkenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "DarkenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "LightenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "LightenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "LightenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "OverlayClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "OverlayClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "OverlayClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "HardLightClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "HardLightClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "HardLightClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "NormalXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplyXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplyXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplyXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlayXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlayXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlayXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.tt new file mode 100644 index 0000000000..e0652d6799 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.tt @@ -0,0 +1,138 @@ +<# +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ output extension=".cs" #> +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +// +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +internal static partial class AssociatedAlphaPorterDuffFunctions +{ +<# + foreach (string composer in Composers) + { + foreach (string blender in Blenders) + { + string function = blender + composer; + + foreach (string vectorType in VectorTypes) + { + string opacityType = vectorType == "Vector4" ? "float" : vectorType; + string zero = vectorType == "Vector4" ? "Vector4.Zero" : vectorType.Replace("", ".Zero"); +#> + /// + /// Returns the associated-alpha result of the "<#= function #>" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= vectorType #> <#= function #>(<#= vectorType #> backdrop, <#= vectorType #> source, <#= opacityType #> opacity) + { +<# + if (composer != "Dest" && composer != "Clear") + { +#> + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + +<# + } +#> + return <#= GetComposition(blender, composer, zero) #>; + } + +<# + } + } + } +#> +} +<#+ + private static readonly string[] Composers = + { + "Src", + "SrcAtop", + "SrcOver", + "SrcIn", + "SrcOut", + "Dest", + "DestAtop", + "DestOver", + "DestIn", + "DestOut", + "Clear", + "Xor", + }; + + private static readonly string[] Blenders = + { + "Normal", + "Multiply", + "Add", + "Subtract", + "Screen", + "Darken", + "Lighten", + "Overlay", + "HardLight", + }; + + private static readonly string[] VectorTypes = + { + "Vector4", + "Vector256", + "Vector512", + }; + + private static string GetComposition(string blender, string composer, string zero) + { + bool normal = blender == "Normal"; + + switch (composer) + { + case "Src": + return "source"; + case "SrcAtop": + return normal + ? "AtopNormal(backdrop, source)" + : $"Atop(backdrop, source, {blender}(backdrop, source))"; + case "SrcOver": + return normal + ? "OverNormal(backdrop, source)" + : $"Over(backdrop, source, {blender}(backdrop, source))"; + case "SrcIn": + return "In(backdrop, source)"; + case "SrcOut": + return "Out(backdrop, source)"; + case "Dest": + return "backdrop"; + case "DestAtop": + return normal + ? "AtopNormal(source, backdrop)" + : $"Atop(source, backdrop, {blender}(source, backdrop))"; + case "DestOver": + return normal + ? "OverNormal(source, backdrop)" + : $"Over(source, backdrop, {blender}(source, backdrop))"; + case "DestIn": + return "In(source, backdrop)"; + case "DestOut": + return "Out(source, backdrop)"; + case "Clear": + return zero; + default: + return "Xor(backdrop, source)"; + } + } +#> diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.cs new file mode 100644 index 0000000000..4f6af32c8a --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.cs @@ -0,0 +1,546 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +/// +/// Provides Porter-Duff composition functions for associated-alpha vectors. +/// +internal static partial class AssociatedAlphaPorterDuffFunctions +{ + private const int BlendAlphaControl = 0b_10_00_10_00; + private const int ShuffleAlphaControl = 0b_11_11_11_11; + + /// + /// Calculates the associated overlap term for Multiply blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Multiply(Vector4 backdrop, Vector4 source) => backdrop * source; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Multiply(Vector256 backdrop, Vector256 source) => backdrop * source; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Multiply(Vector512 backdrop, Vector512 source) => backdrop * source; + + /// + /// Calculates the associated overlap term for Add blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Add(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + return Vector4.Min(backdropAlpha * sourceAlpha, (backdrop * sourceAlpha) + (source * backdropAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Add(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + return Vector256.Min(backdropAlpha * sourceAlpha, (backdrop * sourceAlpha) + (source * backdropAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Add(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + return Vector512.Min(backdropAlpha * sourceAlpha, (backdrop * sourceAlpha) + (source * backdropAlpha)); + } + + /// + /// Calculates the associated overlap term for Subtract blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Subtract(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + return Vector4.Max(Vector4.Zero, (backdrop * sourceAlpha) - (source * backdropAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Subtract(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + return Vector256.Max(Vector256.Zero, (backdrop * sourceAlpha) - (source * backdropAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Subtract(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + return Vector512.Max(Vector512.Zero, (backdrop * sourceAlpha) - (source * backdropAlpha)); + } + + /// + /// Calculates the associated overlap term for Screen blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Screen(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + return (backdrop * sourceAlpha) + (source * backdropAlpha) - (backdrop * source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Screen(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + return (backdrop * sourceAlpha) + (source * backdropAlpha) - (backdrop * source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Screen(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + return (backdrop * sourceAlpha) + (source * backdropAlpha) - (backdrop * source); + } + + /// + /// Calculates the associated overlap term for Darken blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Darken(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + return Vector4.Min(backdrop * sourceAlpha, source * backdropAlpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Darken(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + return Vector256.Min(backdrop * sourceAlpha, source * backdropAlpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Darken(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + return Vector512.Min(backdrop * sourceAlpha, source * backdropAlpha); + } + + /// + /// Calculates the associated overlap term for Lighten blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Lighten(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + return Vector4.Max(backdrop * sourceAlpha, source * backdropAlpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Lighten(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + return Vector256.Max(backdrop * sourceAlpha, source * backdropAlpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Lighten(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + return Vector512.Max(backdrop * sourceAlpha, source * backdropAlpha); + } + + /// + /// Calculates the associated overlap term for Overlay blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Overlay(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + + return new Vector4( + OverlayValue(backdrop.X, backdropAlpha.X, source.X, sourceAlpha.X), + OverlayValue(backdrop.Y, backdropAlpha.Y, source.Y, sourceAlpha.Y), + OverlayValue(backdrop.Z, backdropAlpha.Z, source.Z, sourceAlpha.Z), + 0F); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Overlay(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 left = (backdrop + backdrop) * source; + Vector256 right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector256.Create(2F)); + Vector256 useRight = Avx.CompareGreaterThan(backdrop + backdrop, backdropAlpha); + return Avx.BlendVariable(left, right, useRight); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Overlay(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 left = (backdrop + backdrop) * source; + Vector512 right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector512.Create(2F)); + Vector512 useRight = Avx512F.CompareGreaterThan(backdrop + backdrop, backdropAlpha); + return Vector512.ConditionalSelect(useRight, right, left); + } + + /// + /// Calculates the associated overlap term for HardLight blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLight(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + + return new Vector4( + OverlayValue(source.X, sourceAlpha.X, backdrop.X, backdropAlpha.X), + OverlayValue(source.Y, sourceAlpha.Y, backdrop.Y, backdropAlpha.Y), + OverlayValue(source.Z, sourceAlpha.Z, backdrop.Z, backdropAlpha.Z), + 0F); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLight(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 left = (backdrop + backdrop) * source; + Vector256 right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector256.Create(2F)); + Vector256 useRight = Avx.CompareGreaterThan(source + source, sourceAlpha); + return Avx.BlendVariable(left, right, useRight); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLight(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 left = (backdrop + backdrop) * source; + Vector512 right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector512.Create(2F)); + Vector512 useRight = Avx512F.CompareGreaterThan(source + source, sourceAlpha); + return Vector512.ConditionalSelect(useRight, right, left); + } + + /// + /// Composites an associated source over an associated destination without a color-blending function. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverNormal(Vector4 destination, Vector4 source) + { + // Associated source-over is Ps + Pb(1 - As); both color and alpha therefore use the same coefficient. + Vector4 sourceAlpha = Numerics.PermuteW(source); + return source + (destination * (Vector4.One - sourceAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverNormal(Vector256 destination, Vector256 source) + { + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + return source + (destination * (Vector256.Create(1F) - sourceAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverNormal(Vector512 destination, Vector512 source) + { + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + return source + (destination * (Vector512.Create(1F) - sourceAlpha)); + } + + /// + /// Composites an associated source atop an associated destination without a color-blending function. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AtopNormal(Vector4 destination, Vector4 source) + { + // Source-atop retains the destination alpha while replacing its covered contribution with the source. + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 destinationAlpha = Numerics.PermuteW(destination); + return (source * destinationAlpha) + (destination * (Vector4.One - sourceAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AtopNormal(Vector256 destination, Vector256 source) + { + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); + return (source * destinationAlpha) + (destination * (Vector256.Create(1F) - sourceAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AtopNormal(Vector512 destination, Vector512 source) + { + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + return (source * destinationAlpha) + (destination * (Vector512.Create(1F) - sourceAlpha)); + } + + /// + /// Composites an associated source over an associated destination using an unassociated blended color. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated overlap term produced by the color-blending function. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Over(Vector4 destination, Vector4 source, Vector4 overlap) + { + // The three terms cover destination-only, source-only, and overlapping color respectively. + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 destinationAlpha = Numerics.PermuteW(destination); + Vector4 result = (destination * (Vector4.One - sourceAlpha)) + (source * (Vector4.One - destinationAlpha)) + overlap; + Vector4 alpha = source + (destination * (Vector4.One - sourceAlpha)); + return Numerics.WithW(result, alpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Over(Vector256 destination, Vector256 source, Vector256 overlap) + { + Vector256 one = Vector256.Create(1F); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); + Vector256 result = (destination * (one - sourceAlpha)) + (source * (one - destinationAlpha)) + overlap; + Vector256 alpha = source + (destination * (one - sourceAlpha)); + return Avx.Blend(result, alpha, BlendAlphaControl); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Over(Vector512 destination, Vector512 source, Vector512 overlap) + { + Vector512 one = Vector512.Create(1F); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + Vector512 result = (destination * (one - sourceAlpha)) + (source * (one - destinationAlpha)) + overlap; + Vector512 alpha = source + (destination * (one - sourceAlpha)); + return Vector512.ConditionalSelect(AlphaMask512(), alpha, result); + } + + /// + /// Composites an associated source atop an associated destination using an unassociated blended color. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated overlap term produced by the color-blending function. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Atop(Vector4 destination, Vector4 source, Vector4 overlap) + { + // Atop discards source-only color and retains the destination alpha unchanged. + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 destinationAlpha = Numerics.PermuteW(destination); + Vector4 result = (destination * (Vector4.One - sourceAlpha)) + overlap; + return Numerics.WithW(result, destinationAlpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Atop(Vector256 destination, Vector256 source, Vector256 overlap) + { + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); + Vector256 result = (destination * (Vector256.Create(1F) - sourceAlpha)) + overlap; + return Avx.Blend(result, destinationAlpha, BlendAlphaControl); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Atop(Vector512 destination, Vector512 source, Vector512 overlap) + { + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + Vector512 result = (destination * (Vector512.Create(1F) - sourceAlpha)) + overlap; + return Vector512.ConditionalSelect(AlphaMask512(), destinationAlpha, result); + } + + /// + /// Retains the associated source within the destination coverage. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 In(Vector4 destination, Vector4 source) => source * Numerics.PermuteW(destination); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 In(Vector256 destination, Vector256 source) + => source * Avx.Permute(destination, ShuffleAlphaControl); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 In(Vector512 destination, Vector512 source) + => source * Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + + /// + /// Retains the associated source outside the destination coverage. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Out(Vector4 destination, Vector4 source) + => source * (Vector4.One - Numerics.PermuteW(destination)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Out(Vector256 destination, Vector256 source) + => source * (Vector256.Create(1F) - Avx.Permute(destination, ShuffleAlphaControl)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Out(Vector512 destination, Vector512 source) + => source * (Vector512.Create(1F) - Vector512_.ShuffleNative(destination, ShuffleAlphaControl)); + + /// + /// Retains only the non-overlapping parts of two associated vectors. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Xor(Vector4 destination, Vector4 source) + { + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 destinationAlpha = Numerics.PermuteW(destination); + return (source * (Vector4.One - destinationAlpha)) + (destination * (Vector4.One - sourceAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Xor(Vector256 destination, Vector256 source) + { + Vector256 one = Vector256.Create(1F); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); + return (source * (one - destinationAlpha)) + (destination * (one - sourceAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Xor(Vector512 destination, Vector512 source) + { + Vector512 one = Vector512.Create(1F); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + return (source * (one - destinationAlpha)) + (destination * (one - sourceAlpha)); + } + + /// + /// Applies raster coverage to an associated composition result. + /// + /// The associated backdrop vector. + /// The associated composition result. + /// The raster coverage in the range 0 through 1. + /// The covered associated result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 BlendWithCoverage(Vector4 backdrop, Vector4 source, float coverage) + { + // Use the same fused operation as the wider paths so exact midpoints cannot change across vector widths. + return Vector128_.MultiplyAdd(backdrop.AsVector128(), (source - backdrop).AsVector128(), Vector128.Create(coverage)).AsVector4(); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 BlendWithCoverage(Vector256 backdrop, Vector256 source, Vector256 coverage) + => Vector256_.MultiplyAdd(backdrop, source - backdrop, coverage); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 BlendWithCoverage(Vector512 backdrop, Vector512 source, Vector512 coverage) + => Vector512_.MultiplyAdd(backdrop, source - backdrop, coverage); + + /// + /// Calculates one associated Overlay overlap component without recovering either straight component. + /// + /// The associated backdrop component. + /// The backdrop alpha. + /// The associated source component. + /// The source alpha. + /// The associated overlap component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float OverlayValue(float backdrop, float backdropAlpha, float source, float sourceAlpha) + { + // Comparing 2Pb with Ab is equivalent to comparing the straight backdrop component with one half. + return (backdrop + backdrop) <= backdropAlpha + ? (backdrop + backdrop) * source + : (backdropAlpha * sourceAlpha) - (2F * (backdropAlpha - backdrop) * (sourceAlpha - source)); + } + + /// + /// Creates a SIMD lane mask selecting the alpha component of each packed vector. + /// + /// The alpha-component mask. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 AlphaMask512() + => Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index b764432e8a..1e5fc8df91 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -338,7 +338,9 @@ internal static partial class PorterDuffFunctions Vector4 sourceAlpha = Numerics.PermuteW(source); Vector4 backdropPremultiplied = Numerics.WithW(backdrop * backdropAlpha, backdropAlpha); Vector4 sourcePremultiplied = Numerics.WithW(source * sourceAlpha, sourceAlpha); - Vector4 result = backdropPremultiplied + ((sourcePremultiplied - backdropPremultiplied) * coverage); + + // Use the same fused operation as the wider paths so exact midpoints cannot change across vector widths. + Vector4 result = Vector128_.MultiplyAdd(backdropPremultiplied.AsVector128(), (sourcePremultiplied - backdropPremultiplied).AsVector128(), Vector128.Create(coverage)).AsVector4(); Numerics.UnPremultiply(ref result); return result; diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 0b1a51d74f..47a18f54c0 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -93,12 +93,12 @@ public abstract class PixelBlender Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); Span sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength); - PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); - PixelOperations.Instance.ToVector4(configuration, source[..maxLength], sourceVectors, PixelConversionModifiers.Scale); + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); + this.ToBlendVector4(configuration, source[..maxLength], sourceVectors); this.BlendFunction(destinationVectors, backgroundVectors, sourceVectors, amount); - PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); + this.FromBlendVector4(configuration, destinationVectors, destination); } /// @@ -161,11 +161,11 @@ public abstract class PixelBlender Span destinationVectors = workingBuffer[..maxLength]; Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); - PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); - this.BlendFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount); + this.BlendFunction(destinationVectors, backgroundVectors, this.ToBlendVector4(source), amount); - PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); + this.FromBlendVector4(configuration, destinationVectors, destination); } /// @@ -242,12 +242,12 @@ public abstract class PixelBlender Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); Span sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength); - PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); - PixelOperations.Instance.ToVector4(configuration, source[..maxLength], sourceVectors, PixelConversionModifiers.Scale); + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); + this.ToBlendVector4(configuration, source[..maxLength], sourceVectors); this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, sourceVectors, amount, coverage); - PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); + this.FromBlendVector4(configuration, destinationVectors, destination); } /// @@ -317,11 +317,11 @@ public abstract class PixelBlender Span destinationVectors = workingBuffer[..maxLength]; Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); - PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); - this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount, coverage); + this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, this.ToBlendVector4(source), amount, coverage); - PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); + this.FromBlendVector4(configuration, destinationVectors, destination); } /// @@ -463,12 +463,12 @@ public abstract class PixelBlender Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); Span sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength); - PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); - PixelOperations.Instance.ToVector4(configuration, source[..maxLength], sourceVectors, PixelConversionModifiers.Scale); + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); + this.ToBlendVector4(configuration, source[..maxLength], sourceVectors); this.BlendFunction(destinationVectors, backgroundVectors, sourceVectors, amount); - PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); + this.FromBlendVector4(configuration, destinationVectors, destination); } /// @@ -499,11 +499,11 @@ public abstract class PixelBlender Span destinationVectors = workingBuffer[..maxLength]; Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); - PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); - this.BlendFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount); + this.BlendFunction(destinationVectors, backgroundVectors, this.ToBlendVector4(source), amount); - PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); + this.FromBlendVector4(configuration, destinationVectors, destination); } /// @@ -660,12 +660,12 @@ public abstract class PixelBlender Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); Span sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength); - PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); - PixelOperations.Instance.ToVector4(configuration, source[..maxLength], sourceVectors, PixelConversionModifiers.Scale); + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); + this.ToBlendVector4(configuration, source[..maxLength], sourceVectors); this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, sourceVectors, amount, coverage); - PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); + this.FromBlendVector4(configuration, destinationVectors, destination); } /// @@ -699,13 +699,47 @@ public abstract class PixelBlender Span destinationVectors = workingBuffer[..maxLength]; Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); - PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); - this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount, coverage); + this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, this.ToBlendVector4(source), amount, coverage); - PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); + this.FromBlendVector4(configuration, destinationVectors, destination); } + /// + /// Converts source pixels to the scaled-vector representation consumed by this blender. + /// + /// The source pixel format. + /// The configuration. + /// The source pixels. + /// The destination vectors. + protected virtual void ToBlendVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination) + where TPixelSource : unmanaged, IPixel + => PixelOperations.Instance.ToUnassociatedScaledVector4(configuration, source, destination); + + /// + /// Converts a source pixel to the vector representation consumed by the blend functions. + /// + /// The source pixel. + /// The source vector. + protected virtual Vector4 ToBlendVector4(TPixel source) + => PixelOperations.Instance.ToUnassociatedScaledVector4(source); + + /// + /// Converts blend results from this blender's scaled-vector representation to destination pixels. + /// + /// The configuration. + /// The source vectors. + /// The destination pixels. + protected virtual void FromBlendVector4( + Configuration configuration, + Span source, + Span destination) + => PixelOperations.Instance.FromUnassociatedScaledVector4(configuration, source, destination); + /// /// Blend 2 rows together. /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32P.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32P.cs new file mode 100644 index 0000000000..780dbd2f8f --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32P.cs @@ -0,0 +1,241 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Packed pixel type containing alpha and associated blue, green, and red components as 8-bit unsigned normalized values. +/// Components are stored in alpha, blue, green, and red order from least to most significant byte. +/// +/// +/// Component, packed, and vector values use associated alpha representation. +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct Abgr32P : IPixel, IPackedVector +{ + private const float ByteScale = 1F / byte.MaxValue; + + private static readonly Vector4 Half = new(0.5F); + private static readonly Vector4 MaxBytes = new(byte.MaxValue); + + /// + /// Gets or sets the alpha component. + /// + public byte A; + + /// + /// Gets or sets the associated blue component. + /// + public byte B; + + /// + /// Gets or sets the associated green component. + /// + public byte G; + + /// + /// Gets or sets the associated red component. + /// + public byte R; + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Abgr32P(byte r, byte g, byte b) + : this(r, g, b, byte.MaxValue) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Abgr32P(byte r, byte g, byte b, byte a) + { + this.A = a; + this.B = b; + this.G = g; + this.R = r; + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Abgr32P(float r, float g, float b) + : this(new Vector4(r, g, b, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Abgr32P(float r, float g, float b, float a) + : this(new Vector4(r, g, b, a)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Abgr32P(Vector3 vector) + : this(new Vector4(vector, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Abgr32P(Vector4 vector) + : this() => this = FromScaledVector4(vector); + + /// + /// Initializes a new instance of the struct from a packed associated value. + /// + /// The packed associated value. + public Abgr32P(uint packed) + : this() => this.PackedValue = packed; + + /// + public uint PackedValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(Abgr32P left, Abgr32P right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(Abgr32P left, Abgr32P right) => !left.Equals(right); + + /// + public readonly Rgba32 ToRgba32() + => Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A)); + + /// + public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) * ByteScale; + + /// + public readonly Vector4 ToVector4() => this.ToScaledVector4(); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.Alpha | PixelColorType.BGR, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32P FromScaledVector4(Vector4 source) => Pack(source); + + /// + public static Abgr32P FromVector4(Vector4 source) => FromScaledVector4(source); + + /// + public static Abgr32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is Abgr32P other && this.Equals(other); + + /// + public readonly bool Equals(Abgr32P other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() => $"Abgr32P({this.R}, {this.G}, {this.B}, {this.A})"; + + /// + /// Converts an unassociated scaled vector to associated representation. + /// + /// The unassociated scaled vector. + /// The associated pixel. + private static Abgr32P FromUnassociatedScaledVector4(Vector4 source) + => FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.Associate(source)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Abgr32P Pack(Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new Abgr32P(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32P.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32P.cs new file mode 100644 index 0000000000..fac4f77018 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32P.cs @@ -0,0 +1,241 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Packed pixel type containing alpha and associated red, green, and blue components as 8-bit unsigned normalized values. +/// Components are stored in alpha, red, green, and blue order from least to most significant byte. +/// +/// +/// Component, packed, and vector values use associated alpha representation. +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct Argb32P : IPixel, IPackedVector +{ + private const float ByteScale = 1F / byte.MaxValue; + + private static readonly Vector4 Half = new(0.5F); + private static readonly Vector4 MaxBytes = new(byte.MaxValue); + + /// + /// Gets or sets the alpha component. + /// + public byte A; + + /// + /// Gets or sets the associated red component. + /// + public byte R; + + /// + /// Gets or sets the associated green component. + /// + public byte G; + + /// + /// Gets or sets the associated blue component. + /// + public byte B; + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Argb32P(byte r, byte g, byte b) + : this(r, g, b, byte.MaxValue) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Argb32P(byte r, byte g, byte b, byte a) + { + this.A = a; + this.R = r; + this.G = g; + this.B = b; + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Argb32P(float r, float g, float b) + : this(new Vector4(r, g, b, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Argb32P(float r, float g, float b, float a) + : this(new Vector4(r, g, b, a)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Argb32P(Vector3 vector) + : this(new Vector4(vector, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Argb32P(Vector4 vector) + : this() => this = FromScaledVector4(vector); + + /// + /// Initializes a new instance of the struct from a packed associated value. + /// + /// The packed associated value. + public Argb32P(uint packed) + : this() => this.PackedValue = packed; + + /// + public uint PackedValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(Argb32P left, Argb32P right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(Argb32P left, Argb32P right) => !left.Equals(right); + + /// + public readonly Rgba32 ToRgba32() + => Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A)); + + /// + public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) * ByteScale; + + /// + public readonly Vector4 ToVector4() => this.ToScaledVector4(); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.Alpha | PixelColorType.RGB, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32P FromScaledVector4(Vector4 source) => Pack(source); + + /// + public static Argb32P FromVector4(Vector4 source) => FromScaledVector4(source); + + /// + public static Argb32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is Argb32P other && this.Equals(other); + + /// + public readonly bool Equals(Argb32P other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() => $"Argb32P({this.R}, {this.G}, {this.B}, {this.A})"; + + /// + /// Converts an unassociated scaled vector to associated representation. + /// + /// The unassociated scaled vector. + /// The associated pixel. + private static Argb32P FromUnassociatedScaledVector4(Vector4 source) + => FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.Associate(source)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Argb32P Pack(Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new Argb32P(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32P.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32P.cs new file mode 100644 index 0000000000..a69dfeb46f --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32P.cs @@ -0,0 +1,241 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Packed pixel type containing associated blue, green, red, and alpha components as 8-bit unsigned normalized values. +/// Components are stored in blue, green, red, and alpha order from least to most significant byte. +/// +/// +/// Component, packed, and vector values use associated alpha representation. +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct Bgra32P : IPixel, IPackedVector +{ + private const float ByteScale = 1F / byte.MaxValue; + + private static readonly Vector4 Half = new(0.5F); + private static readonly Vector4 MaxBytes = new(byte.MaxValue); + + /// + /// Gets or sets the associated blue component. + /// + public byte B; + + /// + /// Gets or sets the associated green component. + /// + public byte G; + + /// + /// Gets or sets the associated red component. + /// + public byte R; + + /// + /// Gets or sets the alpha component. + /// + public byte A; + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Bgra32P(byte r, byte g, byte b) + : this(r, g, b, byte.MaxValue) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Bgra32P(byte r, byte g, byte b, byte a) + { + this.B = b; + this.G = g; + this.R = r; + this.A = a; + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Bgra32P(float r, float g, float b) + : this(new Vector4(r, g, b, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Bgra32P(float r, float g, float b, float a) + : this(new Vector4(r, g, b, a)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Bgra32P(Vector3 vector) + : this(new Vector4(vector, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Bgra32P(Vector4 vector) + : this() => this = FromScaledVector4(vector); + + /// + /// Initializes a new instance of the struct from a packed associated value. + /// + /// The packed associated value. + public Bgra32P(uint packed) + : this() => this.PackedValue = packed; + + /// + public uint PackedValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(Bgra32P left, Bgra32P right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(Bgra32P left, Bgra32P right) => !left.Equals(right); + + /// + public readonly Rgba32 ToRgba32() + => Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A)); + + /// + public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) * ByteScale; + + /// + public readonly Vector4 ToVector4() => this.ToScaledVector4(); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.BGR | PixelColorType.Alpha, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32P FromScaledVector4(Vector4 source) => Pack(source); + + /// + public static Bgra32P FromVector4(Vector4 source) => FromScaledVector4(source); + + /// + public static Bgra32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is Bgra32P other && this.Equals(other); + + /// + public readonly bool Equals(Bgra32P other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() => $"Bgra32P({this.R}, {this.G}, {this.B}, {this.A})"; + + /// + /// Converts an unassociated scaled vector to associated representation. + /// + /// The unassociated scaled vector. + /// The associated pixel. + private static Bgra32P FromUnassociatedScaledVector4(Vector4 source) + => FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.Associate(source)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Bgra32P Pack(Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new Bgra32P(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4P.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4P.cs new file mode 100644 index 0000000000..7b3bda330f --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4P.cs @@ -0,0 +1,243 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Packed pixel type containing four associated 16-bit floating-point values. +/// +/// +/// Packed and vector values use associated alpha representation. +/// +public partial struct HalfVector4P : IPixel, IPackedVector +{ + /// + /// Initializes a new instance of the struct. + /// + /// The associated x-component. + /// The associated y-component. + /// The associated z-component. + /// The alpha component. + public HalfVector4P(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the associated component values. + public HalfVector4P(Vector4 vector) => this.PackedValue = Pack(vector); + + /// + public ulong PackedValue { get; set; } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(HalfVector4P left, HalfVector4P right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(HalfVector4P left, HalfVector4P right) => !left.Equals(right); + + /// + public readonly Rgba32 ToRgba32() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.UnPremultiply(ref vector); + return Rgba32.FromScaledVector4(vector); + } + + /// + public readonly Vector4 ToScaledVector4() + { + Vector4 scaled = this.ToVector4(); + scaled += Vector4.One; + scaled /= 2F; + return scaled; + } + + /// + public readonly Vector4 ToVector4() => new( + HalfTypeHelper.Unpack((ushort)this.PackedValue), + HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)), + HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x20)), + HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x30))); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + public static HalfVector4P FromScaledVector4(Vector4 source) + { + source *= 2F; + source -= Vector4.One; + return FromVector4(source); + } + + /// + public static HalfVector4P FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; + + /// + public static HalfVector4P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is HalfVector4P other && this.Equals(other); + + /// + public readonly bool Equals(HalfVector4P other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"HalfVector4P({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + /// + /// Converts an unassociated scaled vector to associated representation. + /// + /// The unassociated scaled vector. + /// The associated pixel. + private static HalfVector4P FromUnassociatedScaledVector4(Vector4 source) + => FromScaledVector4(Associate(source)); + + /// + /// Converts an unassociated scaled vector to the associated representation of a half-precision destination. + /// + /// The unassociated scaled vector. + /// The associated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Associate(Vector4 source) + { + // Quantize alpha through the destination's native half representation before RGB is associated with it. + float nativeAlpha = (source.W * 2F) - 1F; + source.W = (HalfTypeHelper.Unpack(HalfTypeHelper.Pack(nativeAlpha)) + 1F) / 2F; + Numerics.Premultiply(ref source); + return source; + } + + /// + /// Converts unassociated scaled vectors to the associated representation of a half-precision destination. + /// + /// The vectors to convert in place. + private static void Associate(Span source) + { + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(source); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Unsafe.Add(ref sourceBase, i) = Associate(Unsafe.Add(ref sourceBase, i)); + } + } + + /// + /// Reassociates a scaled vector with the alpha value the destination stores. + /// + /// The associated scaled vector. + /// The reassociated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Reassociate(Vector4 source) + { + float alpha = source.W; + + if (alpha == 0) + { + return Vector4.Zero; + } + + float nativeAlpha = (alpha * 2F) - 1F; + float storedAlpha = (HalfTypeHelper.Unpack(HalfTypeHelper.Pack(nativeAlpha)) + 1F) / 2F; + + // Associated RGB scales by the same ratio as alpha. Applying that ratio directly avoids the extra division and multiplication of an unpremultiply/premultiply round trip and preserves exact midpoints when alpha needs no quantization. + source *= storedAlpha / alpha; + source.W = storedAlpha; + return source; + } + + /// + /// Reassociates scaled vectors with the alpha values the destination stores. + /// + /// The vectors to convert in place. + private static void Reassociate(Span source) + { + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(source); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Unsafe.Add(ref sourceBase, i) = Reassociate(Unsafe.Add(ref sourceBase, i)); + } + } + + /// + /// Packs native half-precision components into a 64-bit value. + /// + /// The native component values. + /// The packed value. + private static ulong Pack(Vector4 vector) + { + ulong x = HalfTypeHelper.Pack(vector.X); + ulong y = (ulong)HalfTypeHelper.Pack(vector.Y) << 0x10; + ulong z = (ulong)HalfTypeHelper.Pack(vector.Z) << 0x20; + ulong w = (ulong)HalfTypeHelper.Pack(vector.W) << 0x30; + return x | y | z | w; + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4P.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4P.cs new file mode 100644 index 0000000000..6a31827778 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4P.cs @@ -0,0 +1,276 @@ +// 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; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Packed pixel type containing four associated 8-bit signed normalized values ranging from -1 to 1. +/// +/// +/// Packed and vector values use associated alpha representation. +/// +public partial struct NormalizedByte4P : IPixel, IPackedVector +{ + private const float MaxPos = 127F; + private const float ScaledMagnitude = MaxPos * 2F; + private static readonly Vector4 Half = Vector128.Create(MaxPos).AsVector4(); + private static readonly Vector4 MinusOne = Vector128.Create(-1F).AsVector4(); + + /// + /// Initializes a new instance of the struct. + /// + /// The associated x-component. + /// The associated y-component. + /// The associated z-component. + /// The alpha component. + public NormalizedByte4P(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the associated component values. + public NormalizedByte4P(Vector4 vector) => this.PackedValue = Pack(vector); + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(NormalizedByte4P left, NormalizedByte4P right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(NormalizedByte4P left, NormalizedByte4P right) => !left.Equals(right); + + /// + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(ToUnassociatedScaledVector4(this)); + + /// + public readonly Vector4 ToScaledVector4() + { + Vector4 scaled = this.ToVector4(); + scaled += Vector4.One; + scaled /= 2F; + return scaled; + } + + /// + public readonly Vector4 ToVector4() => new( + (sbyte)((this.PackedValue >> 0) & 0xFF) / MaxPos, + (sbyte)((this.PackedValue >> 8) & 0xFF) / MaxPos, + (sbyte)((this.PackedValue >> 16) & 0xFF) / MaxPos, + (sbyte)((this.PackedValue >> 24) & 0xFF) / MaxPos); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + public static NormalizedByte4P FromScaledVector4(Vector4 source) + { + source *= 2F; + source -= Vector4.One; + return FromVector4(source); + } + + /// + public static NormalizedByte4P FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; + + /// + public static NormalizedByte4P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is NormalizedByte4P other && this.Equals(other); + + /// + public readonly bool Equals(NormalizedByte4P other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"NormalizedByte4P({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + /// + /// Converts an unassociated scaled vector to associated representation. + /// + /// The unassociated scaled vector. + /// The associated pixel. + private static NormalizedByte4P FromUnassociatedScaledVector4(Vector4 source) + { + return FromScaledVector4(Associate(source)); + } + + /// + /// Converts an unassociated scaled vector to the associated representation of a signed-normalized-byte destination. + /// + /// The unassociated scaled vector. + /// The associated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Associate(Vector4 source) + { + // Reproduce the signed-normalized packer's alpha quantization, then associate RGB with the exact alpha that will be stored. + float nativeAlpha = Numerics.Clamp((source.W * 2F) - 1F, -1F, 1F); + float storedAlpha = MathF.Round(nativeAlpha * MaxPos); + source.W = (storedAlpha + MaxPos) / ScaledMagnitude; + Numerics.Premultiply(ref source); + return source; + } + + /// + /// Converts the stored associated components to an unassociated scaled vector. + /// + /// The associated pixel. + /// The unassociated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 ToUnassociatedScaledVector4(NormalizedByte4P source) + { + // Offset signed storage into exact nonnegative byte magnitudes before division so the quotient retains the destination's byte-rounding midpoint. + Vector4 vector = new( + (sbyte)(source.PackedValue >> 0) + MaxPos, + (sbyte)(source.PackedValue >> 8) + MaxPos, + (sbyte)(source.PackedValue >> 16) + MaxPos, + (sbyte)(source.PackedValue >> 24) + MaxPos); + + if (vector.W == 0F) + { + // Numerics.UnPremultiply preserves RGB when alpha is zero. Normalize the stored components because they already are the unassociated value in this case. + return vector / ScaledMagnitude; + } + + Numerics.UnPremultiply(ref vector); + vector.W /= ScaledMagnitude; + return vector; + } + + /// + /// Converts unassociated scaled vectors to the associated representation of a signed-normalized-byte destination. + /// + /// The vectors to convert in place. + private static void Associate(Span source) + { + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(source); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Unsafe.Add(ref sourceBase, i) = Associate(Unsafe.Add(ref sourceBase, i)); + } + } + + /// + /// Reassociates a scaled vector with the alpha value the destination stores. + /// + /// The associated scaled vector. + /// The reassociated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Reassociate(Vector4 source) + { + float alpha = source.W; + + if (alpha == 0) + { + return Vector4.Zero; + } + + float nativeAlpha = Numerics.Clamp((alpha * 2F) - 1F, -1F, 1F); + float storedAlpha = (MathF.Round(nativeAlpha * MaxPos) + MaxPos) / ScaledMagnitude; + + // Associated RGB scales by the same ratio as alpha. Applying that ratio directly avoids the extra division and multiplication of an unpremultiply/premultiply round trip and preserves exact midpoints when alpha needs no quantization. + source *= storedAlpha / alpha; + source.W = storedAlpha; + return source; + } + + /// + /// Reassociates scaled vectors with the alpha values the destination stores. + /// + /// The vectors to convert in place. + private static void Reassociate(Span source) + { + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(source); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Unsafe.Add(ref sourceBase, i) = Reassociate(Unsafe.Add(ref sourceBase, i)); + } + } + + /// + /// Packs native signed normalized components into a 32-bit value. + /// + /// The native component values. + /// The packed value. + private static uint Pack(Vector4 vector) + { + vector = Numerics.Clamp(vector, MinusOne, Vector4.One) * Half; + + uint byte4 = ((uint)Convert.ToInt16(MathF.Round(vector.X)) & 0xFF) << 0; + uint byte3 = ((uint)Convert.ToInt16(MathF.Round(vector.Y)) & 0xFF) << 8; + uint byte2 = ((uint)Convert.ToInt16(MathF.Round(vector.Z)) & 0xFF) << 16; + uint byte1 = ((uint)Convert.ToInt16(MathF.Round(vector.W)) & 0xFF) << 24; + + return byte4 | byte3 | byte2 | byte1; + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32P.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32P.PixelOperations.cs new file mode 100644 index 0000000000..0888130f74 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32P.PixelOperations.cs @@ -0,0 +1,66 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides optimized overrides for bulk operations. +/// +public partial struct Abgr32P +{ + /// + /// Provides optimized bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + /// + internal override Vector4 ToUnassociatedScaledVector4(Abgr32P source) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source.R, source.G, source.B, source.A); + + /// + internal override Abgr32P FromUnassociatedScaledVector4(Vector4 source) + => FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.Associate(source)); + + /// + public override Abgr32P FromAssociatedScaledVector4(Vector4 source) + => Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4ToAbgr32P(source); + + /// + internal override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); + + /// + internal override void FromUnassociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + Vector4Converters.AssociatedRgbaCompatible.Associate(source); + this.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale); + } + + /// + public override void FromAssociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); + } + + /// + public override void ToVector4( + Configuration configuration, + ReadOnlySpan source, + Span destinationVectors, + PixelConversionModifiers modifiers) + => Vector4Converters.AssociatedRgbaCompatible.ToVector4(source, destinationVectors, modifiers); + + /// + public override void FromVector4Destructive( + Configuration configuration, + Span sourceVectors, + Span destination, + PixelConversionModifiers modifiers) + => Vector4Converters.AssociatedRgbaCompatible.FromVector4(sourceVectors, destination, modifiers); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32P.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32P.PixelOperations.cs new file mode 100644 index 0000000000..472f22dc6c --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32P.PixelOperations.cs @@ -0,0 +1,66 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides optimized overrides for bulk operations. +/// +public partial struct Argb32P +{ + /// + /// Provides optimized bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + /// + internal override Vector4 ToUnassociatedScaledVector4(Argb32P source) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source.R, source.G, source.B, source.A); + + /// + internal override Argb32P FromUnassociatedScaledVector4(Vector4 source) + => FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.Associate(source)); + + /// + public override Argb32P FromAssociatedScaledVector4(Vector4 source) + => Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4ToArgb32P(source); + + /// + internal override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); + + /// + internal override void FromUnassociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + Vector4Converters.AssociatedRgbaCompatible.Associate(source); + this.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale); + } + + /// + public override void FromAssociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); + } + + /// + public override void ToVector4( + Configuration configuration, + ReadOnlySpan source, + Span destinationVectors, + PixelConversionModifiers modifiers) + => Vector4Converters.AssociatedRgbaCompatible.ToVector4(source, destinationVectors, modifiers); + + /// + public override void FromVector4Destructive( + Configuration configuration, + Span sourceVectors, + Span destination, + PixelConversionModifiers modifiers) + => Vector4Converters.AssociatedRgbaCompatible.FromVector4(sourceVectors, destination, modifiers); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32P.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32P.PixelOperations.cs new file mode 100644 index 0000000000..f814934693 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32P.PixelOperations.cs @@ -0,0 +1,66 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides optimized overrides for bulk operations. +/// +public partial struct Bgra32P +{ + /// + /// Provides optimized bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + /// + internal override Vector4 ToUnassociatedScaledVector4(Bgra32P source) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source.R, source.G, source.B, source.A); + + /// + internal override Bgra32P FromUnassociatedScaledVector4(Vector4 source) + => FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.Associate(source)); + + /// + public override Bgra32P FromAssociatedScaledVector4(Vector4 source) + => Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4ToBgra32P(source); + + /// + internal override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); + + /// + internal override void FromUnassociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + Vector4Converters.AssociatedRgbaCompatible.Associate(source); + this.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale); + } + + /// + public override void FromAssociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); + } + + /// + public override void ToVector4( + Configuration configuration, + ReadOnlySpan source, + Span destinationVectors, + PixelConversionModifiers modifiers) + => Vector4Converters.AssociatedRgbaCompatible.ToVector4(source, destinationVectors, modifiers); + + /// + public override void FromVector4Destructive( + Configuration configuration, + Span sourceVectors, + Span destination, + PixelConversionModifiers modifiers) + => Vector4Converters.AssociatedRgbaCompatible.FromVector4(sourceVectors, destination, modifiers); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs index e3ff3a7fea..773b8c1b89 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs @@ -7,7 +7,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats.Utils; - namespace SixLabors.ImageSharp.PixelFormats; /// @@ -45,7 +44,7 @@ public partial struct Bgr24 Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Scale)); } /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs index fd6465a22f..5c8aeea767 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs @@ -7,7 +7,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats.Utils; - namespace SixLabors.ImageSharp.PixelFormats; /// @@ -45,7 +44,7 @@ public partial struct Rgb24 Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Scale)); } /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude index d53ed520b6..abf077d152 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude @@ -147,15 +147,19 @@ using SixLabors.ImageSharp.PixelFormats.Utils; void GenerateRgba32CompatibleVector4ConversionMethods(string pixelType, bool hasAlpha) { - string removeTheseModifiers = "PixelConversionModifiers.Scale"; + string removeFromModifiers = "PixelConversionModifiers.Scale"; + string removeToModifiers = "PixelConversionModifiers.Scale"; + if (!hasAlpha) { - removeTheseModifiers += " | PixelConversionModifiers.Premultiply"; + // Premultiplication cannot change an alpha-less source, but premultiplied input + // must still be unassociated before its alpha channel is discarded. + removeToModifiers += " | PixelConversionModifiers.Premultiply"; } if (hasAlpha) { - GenerateRgba32Compatible32BitVector4ConversionMethods(pixelType, removeTheseModifiers); + GenerateRgba32Compatible32BitVector4ConversionMethods(pixelType, removeFromModifiers); return; } #> @@ -167,7 +171,7 @@ using SixLabors.ImageSharp.PixelFormats.Utils; Span<<#=pixelType#>> destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(<#=removeTheseModifiers#>)); + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(<#=removeFromModifiers#>)); } /// @@ -177,7 +181,7 @@ using SixLabors.ImageSharp.PixelFormats.Utils; Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.ToVector4(configuration, this, source, destination, modifiers.Remove(<#=removeTheseModifiers#>)); + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, source, destination, modifiers.Remove(<#=removeToModifiers#>)); } <#+ } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4P.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4P.PixelOperations.cs new file mode 100644 index 0000000000..39e948fe1b --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4P.PixelOperations.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides bulk operations. +/// +public partial struct HalfVector4P +{ + /// + /// Provides bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + /// + internal override Vector4 ToUnassociatedScaledVector4(HalfVector4P source) + { + Vector4 vector = source.ToScaledVector4(); + Numerics.UnPremultiply(ref vector); + return vector; + } + + /// + internal override HalfVector4P FromUnassociatedScaledVector4(Vector4 source) + => FromScaledVector4(Associate(source)); + + /// + public override HalfVector4P FromAssociatedScaledVector4(Vector4 source) + => FromScaledVector4(Reassociate(source)); + + /// + internal override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + this.ToVector4(configuration, source, destination, PixelConversionModifiers.Scale); + Numerics.UnPremultiply(destination[..source.Length]); + } + + /// + internal override void FromUnassociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + Associate(source); + this.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale); + } + + /// + public override void FromAssociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + Reassociate(source); + this.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale); + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4P.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4P.PixelOperations.cs new file mode 100644 index 0000000000..e8a9b875aa --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4P.PixelOperations.cs @@ -0,0 +1,62 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides bulk operations. +/// +public partial struct NormalizedByte4P +{ + /// + /// Provides bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + /// + internal override Vector4 ToUnassociatedScaledVector4(NormalizedByte4P source) + => NormalizedByte4P.ToUnassociatedScaledVector4(source); + + /// + internal override NormalizedByte4P FromUnassociatedScaledVector4(Vector4 source) + => FromScaledVector4(Associate(source)); + + /// + public override NormalizedByte4P FromAssociatedScaledVector4(Vector4 source) + => FromScaledVector4(Reassociate(source)); + + /// + internal override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + ref NormalizedByte4P sourceBase = ref MemoryMarshal.GetReference(source); + ref Vector4 destinationBase = ref MemoryMarshal.GetReference(destination); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Unsafe.Add(ref destinationBase, i) = NormalizedByte4P.ToUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i)); + } + } + + /// + internal override void FromUnassociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + Associate(source); + this.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale); + } + + /// + public override void FromAssociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + Reassociate(source); + this.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale); + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32P.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32P.PixelOperations.cs new file mode 100644 index 0000000000..7f7386f1d2 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32P.PixelOperations.cs @@ -0,0 +1,66 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides optimized overrides for bulk operations. +/// +public partial struct Rgba32P +{ + /// + /// Provides optimized bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + /// + internal override Vector4 ToUnassociatedScaledVector4(Rgba32P source) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source.R, source.G, source.B, source.A); + + /// + internal override Rgba32P FromUnassociatedScaledVector4(Vector4 source) + => FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.Associate(source)); + + /// + public override Rgba32P FromAssociatedScaledVector4(Vector4 source) + => Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4ToRgba32P(source); + + /// + internal override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); + + /// + internal override void FromUnassociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + Vector4Converters.AssociatedRgbaCompatible.Associate(source); + this.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale); + } + + /// + public override void FromAssociatedScaledVector4(Configuration configuration, Span source, Span destination) + { + source = source[..destination.Length]; + Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); + } + + /// + public override void ToVector4( + Configuration configuration, + ReadOnlySpan source, + Span destinationVectors, + PixelConversionModifiers modifiers) + => Vector4Converters.AssociatedRgbaCompatible.ToVector4(source, destinationVectors, modifiers); + + /// + public override void FromVector4Destructive( + Configuration configuration, + Span sourceVectors, + Span destination, + PixelConversionModifiers modifiers) + => Vector4Converters.AssociatedRgbaCompatible.FromVector4(sourceVectors, destination, modifiers); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs index 5f9b51af90..077164da75 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs @@ -25,7 +25,10 @@ public partial struct RgbaVector { Span destinationVectors = MemoryMarshal.Cast(destinationPixels); - PixelOperations.Instance.ToVector4(configuration, sourcePixels, destinationVectors, PixelConversionModifiers.Scale); + PixelOperations.Instance.ToUnassociatedScaledVector4(configuration, sourcePixels, destinationVectors); + + // RgbaVector.FromScaledVector4 clamps scaled input, so the optimized bulk path must preserve that behavior after unassociating. + Numerics.Clamp(MemoryMarshal.Cast(destinationVectors), 0F, 1F); } /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32P.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32P.cs new file mode 100644 index 0000000000..46b6a4f65d --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32P.cs @@ -0,0 +1,241 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Packed pixel type containing associated red, green, blue, and alpha components as 8-bit unsigned normalized values. +/// Components are stored in red, green, blue, and alpha order from least to most significant byte. +/// +/// +/// Component, packed, and vector values use associated alpha representation. +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct Rgba32P : IPixel, IPackedVector +{ + private const float ByteScale = 1F / byte.MaxValue; + + private static readonly Vector4 Half = new(0.5F); + private static readonly Vector4 MaxBytes = new(byte.MaxValue); + + /// + /// Gets or sets the associated red component. + /// + public byte R; + + /// + /// Gets or sets the associated green component. + /// + public byte G; + + /// + /// Gets or sets the associated blue component. + /// + public byte B; + + /// + /// Gets or sets the alpha component. + /// + public byte A; + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Rgba32P(byte r, byte g, byte b) + : this(r, g, b, byte.MaxValue) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Rgba32P(byte r, byte g, byte b, byte a) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Rgba32P(float r, float g, float b) + : this(new Vector4(r, g, b, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Rgba32P(float r, float g, float b, float a) + : this(new Vector4(r, g, b, a)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Rgba32P(Vector3 vector) + : this(new Vector4(vector, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Rgba32P(Vector4 vector) + : this() => this = FromScaledVector4(vector); + + /// + /// Initializes a new instance of the struct from a packed associated value. + /// + /// The packed associated value. + public Rgba32P(uint packed) + : this() => this.PackedValue = packed; + + /// + public uint PackedValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(Rgba32P left, Rgba32P right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(Rgba32P left, Rgba32P right) => !left.Equals(right); + + /// + public readonly Rgba32 ToRgba32() + => Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A)); + + /// + public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) * ByteScale; + + /// + public readonly Vector4 ToVector4() => this.ToScaledVector4(); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32P FromScaledVector4(Vector4 source) => Pack(source); + + /// + public static Rgba32P FromVector4(Vector4 source) => FromScaledVector4(source); + + /// + public static Rgba32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is Rgba32P other && this.Equals(other); + + /// + public readonly bool Equals(Rgba32P other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() => $"Rgba32P({this.R}, {this.G}, {this.B}, {this.A})"; + + /// + /// Converts an unassociated scaled vector to associated representation. + /// + /// The unassociated scaled vector. + /// The associated pixel. + private static Rgba32P FromUnassociatedScaledVector4(Vector4 source) + => FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.Associate(source)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Rgba32P Pack(Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new Rgba32P(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index ea970a7181..08a3044b7c 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -26,6 +26,59 @@ public partial class PixelOperations public static PixelOperations Instance => LazyInstance.Value; #pragma warning restore CA1000 // Do not declare static members on generic types + /// + /// Converts a pixel to the unassociated scaled vector representation used by color operations. + /// + /// The source pixel. + /// The unassociated scaled vector. + internal virtual Vector4 ToUnassociatedScaledVector4(TPixel source) => source.ToScaledVector4(); + + /// + /// Converts an unassociated scaled vector to a pixel. + /// + /// The unassociated scaled vector. + /// The converted pixel. + internal virtual TPixel FromUnassociatedScaledVector4(Vector4 source) => TPixel.FromScaledVector4(source); + + /// + /// Converts pixels to the unassociated scaled vector representation used by color operations. + /// + /// The configuration. + /// The source pixels. + /// The destination vectors. + internal virtual void ToUnassociatedScaledVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination) + => this.ToVector4(configuration, source, destination, PixelConversionModifiers.Scale); + + /// + /// Converts pixels to associated scaled vectors. + /// + /// The configuration. + /// The source pixels. + /// The destination vectors. + internal virtual void ToAssociatedScaledVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination) + { + this.ToVector4(configuration, source, destination, PixelConversionModifiers.Scale); + Numerics.Premultiply(destination[..source.Length]); + } + + /// + /// Converts unassociated scaled vectors to pixels. + /// + /// The configuration. + /// The source vectors. + /// The destination pixels. + internal virtual void FromUnassociatedScaledVector4( + Configuration configuration, + Span source, + Span destination) + => this.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale); + /// /// Gets the pixel type info for the given . /// @@ -121,12 +174,13 @@ public partial class PixelOperations using IMemoryOwner tempVectors = configuration.MemoryAllocator.Allocate(sliceLength); Span vectorSpan = tempVectors.GetSpan(); + for (int i = 0; i < numberOfSlices; i++) { int start = i * sliceLength; ReadOnlySpan s = source.Slice(start, sliceLength); Span d = destination.Slice(start, sliceLength); - PixelOperations.Instance.ToVector4(configuration, s, vectorSpan, PixelConversionModifiers.Scale); + PixelOperations.Instance.ToUnassociatedScaledVector4(configuration, s, vectorSpan); this.FromVector4Destructive(configuration, vectorSpan, d, PixelConversionModifiers.Scale); } @@ -137,7 +191,7 @@ public partial class PixelOperations ReadOnlySpan s = source[endOfCompleteSlices..]; Span d = destination[endOfCompleteSlices..]; vectorSpan = vectorSpan[..remainder]; - PixelOperations.Instance.ToVector4(configuration, s, vectorSpan, PixelConversionModifiers.Scale); + PixelOperations.Instance.ToUnassociatedScaledVector4(configuration, s, vectorSpan); this.FromVector4Destructive(configuration, vectorSpan, d, PixelConversionModifiers.Scale); } } @@ -159,6 +213,13 @@ public partial class PixelOperations Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + if (typeof(TPixel) == typeof(TDestinationPixel)) + { + // An identical destination stores the same representation, including associated alpha, so copying preserves every packed component without a lossy representation round trip. + MemoryMarshal.Cast(source).CopyTo(destination); + return; + } + PixelOperations.Instance.From(configuration, source, destination); } diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.AssociatedRgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.AssociatedRgbaCompatible.cs new file mode 100644 index 0000000000..7ab839cc51 --- /dev/null +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.AssociatedRgbaCompatible.cs @@ -0,0 +1,595 @@ +// 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; +using SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.PixelFormats.Utils; + +/// +/// Contains . +/// +internal static partial class Vector4Converters +{ + /// + /// Provides efficient batched conversion for four-byte pixel types that store premultiplied alpha. + /// + public static class AssociatedRgbaCompatible + { + /// + /// Converts associated RGBA byte components to an unassociated scaled vector. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + /// The unassociated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector4 ToUnassociatedVector4(byte red, byte green, byte blue, byte alpha) + { + // Divide the original byte magnitudes before normalization so a floating-point intermediate cannot move an exact byte conversion across its rounding midpoint. + Vector4 vector = new(red, green, blue, alpha); + + if (alpha == 0) + { + // Numerics.UnPremultiply preserves RGB when alpha is zero. Normalize the stored components because they already are the unassociated value in this case. + return vector / byte.MaxValue; + } + + Numerics.UnPremultiply(ref vector); + vector.W /= byte.MaxValue; + return vector; + } + + /// + /// Converts an unassociated scaled vector to the associated representation of an unsigned-byte destination. + /// + /// The unassociated scaled vector. + /// The associated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector4 Associate(Vector4 source) + { + float storedAlpha = (byte)Numerics.Clamp((source.W * byte.MaxValue) + 0.5F, 0, byte.MaxValue); + + // Associate in byte magnitude before normalization. Multiplying by a normalized alpha and later restoring byte magnitude can move an exact half-byte across its rounding boundary. + source *= storedAlpha; + source.W = storedAlpha; + return source / byte.MaxValue; + } + + /// + /// Converts unassociated scaled vectors to the associated representation of an unsigned-byte destination. + /// + /// The vectors to convert in place. + internal static void Associate(Span source) + { + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(source); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Unsafe.Add(ref sourceBase, i) = Associate(Unsafe.Add(ref sourceBase, i)); + } + } + + /// + /// Converts an associated scaled vector to associated byte magnitudes using the alpha byte the destination stores. + /// + /// The associated scaled vector. + /// The associated byte magnitudes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 ReassociateToByte(Vector4 source) + { + float alpha = source.W; + + if (alpha == 0) + { + return Vector4.Zero; + } + + float byteAlpha = alpha * byte.MaxValue; + float storedAlpha = (byte)Numerics.Clamp(byteAlpha + 0.5F, 0, byte.MaxValue); + + if (byteAlpha == storedAlpha) + { + // Quantization leaves alpha unchanged, so RGB is already associated correctly. Avoiding division preserves exact byte midpoints produced by scaling stored components. + source *= byte.MaxValue; + source.W = storedAlpha; + return source; + } + + // Recover straight RGB before multiplying by the stored alpha byte. Keeping the association in byte magnitude preserves exact half-byte rounding boundaries. + Numerics.UnPremultiply(ref source); + source *= storedAlpha; + source.W = storedAlpha; + return source; + } + + /// + /// Converts associated scaled vectors to associated byte magnitudes using the alpha bytes the destination stores. + /// + /// The vectors to convert in place. + private static void ReassociateToByte(Span source) + { + if (Avx512F.IsSupported) + { + ref Vector512 vectorSourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector512 sourceEnd = ref Unsafe.Add(ref vectorSourceBase, (uint)source.Length / 4u); + + while (Unsafe.IsAddressLessThan(ref vectorSourceBase, ref sourceEnd)) + { + vectorSourceBase = ReassociateToByte(vectorSourceBase); + vectorSourceBase = ref Unsafe.Add(ref vectorSourceBase, 1); + } + + source = source[(source.Length & ~3)..]; + } + else if (Avx.IsSupported) + { + ref Vector256 vectorSourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector256 sourceEnd = ref Unsafe.Add(ref vectorSourceBase, (uint)source.Length / 2u); + + while (Unsafe.IsAddressLessThan(ref vectorSourceBase, ref sourceEnd)) + { + vectorSourceBase = ReassociateToByte(vectorSourceBase); + vectorSourceBase = ref Unsafe.Add(ref vectorSourceBase, 1); + } + + source = source[(source.Length & ~1)..]; + } + + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(source); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Unsafe.Add(ref sourceBase, i) = ReassociateToByte(Unsafe.Add(ref sourceBase, i)); + } + } + + /// + /// Converts four associated scaled vectors to associated byte magnitudes using the alpha bytes the destination stores. + /// + /// The associated scaled vectors. + /// The associated byte magnitudes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ReassociateToByte(Vector512 source) + { + Vector512 zero = Vector512.Zero; + Vector512 byteMax = Vector512.Create((float)byte.MaxValue); + Vector512 alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); + Vector512 byteAlpha = alpha * byteMax; + Vector512 storedAlpha = Vector512.Floor(Vector512.Min(Vector512.Max(byteAlpha + Vector512.Create(.5F), zero), byteMax)); + Vector512 result = (source / alpha) * storedAlpha; + + // Exact byte alpha values need no reassociation. Multiplying by 255 directly preserves RGB values that already lie on byte midpoints. + result = Vector512.ConditionalSelect(Vector512.Equals(byteAlpha, storedAlpha), source * byteMax, result); + Vector512 alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + result = Vector512.ConditionalSelect(alphaMask, storedAlpha, result); + return Vector512.ConditionalSelect(Vector512.Equals(alpha, zero), zero, result); + } + + /// + /// Converts two associated scaled vectors to associated byte magnitudes using the alpha bytes the destination stores. + /// + /// The associated scaled vectors. + /// The associated byte magnitudes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ReassociateToByte(Vector256 source) + { + Vector256 zero = Vector256.Zero; + Vector256 byteMax = Vector256.Create((float)byte.MaxValue); + Vector256 alpha = Avx.Permute(source, 0b_11_11_11_11); + Vector256 byteAlpha = alpha * byteMax; + Vector256 storedAlpha = Avx.Floor(Avx.Min(Avx.Max(byteAlpha + Vector256.Create(.5F), zero), byteMax)); + Vector256 result = (source / alpha) * storedAlpha; + + // Exact byte alpha values need no reassociation. Multiplying by 255 directly preserves RGB values that already lie on byte midpoints. + result = Avx.BlendVariable(result, source * byteMax, Avx.CompareEqual(byteAlpha, storedAlpha)); + result = Avx.Blend(result, storedAlpha, 0b_1000_1000); + return Avx.BlendVariable(result, zero, Avx.CompareEqual(alpha, zero)); + } + + /// + /// Converts an associated scaled vector to an pixel. + /// + /// The associated scaled vector. + /// The associated pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Rgba32P FromAssociatedVector4ToRgba32P(Vector4 source) + { + source = ReassociateToByte(source); + return new Rgba32P(ConvertToByte(source.X), ConvertToByte(source.Y), ConvertToByte(source.Z), ConvertToByte(source.W)); + } + + /// + /// Converts an associated scaled vector to a pixel. + /// + /// The associated scaled vector. + /// The associated pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Bgra32P FromAssociatedVector4ToBgra32P(Vector4 source) + { + source = ReassociateToByte(source); + return new Bgra32P(ConvertToByte(source.X), ConvertToByte(source.Y), ConvertToByte(source.Z), ConvertToByte(source.W)); + } + + /// + /// Converts an associated scaled vector to an pixel. + /// + /// The associated scaled vector. + /// The associated pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Argb32P FromAssociatedVector4ToArgb32P(Vector4 source) + { + source = ReassociateToByte(source); + return new Argb32P(ConvertToByte(source.X), ConvertToByte(source.Y), ConvertToByte(source.Z), ConvertToByte(source.W)); + } + + /// + /// Converts an associated scaled vector to an pixel. + /// + /// The associated scaled vector. + /// The associated pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Abgr32P FromAssociatedVector4ToAbgr32P(Vector4 source) + { + source = ReassociateToByte(source); + return new Abgr32P(ConvertToByte(source.X), ConvertToByte(source.Y), ConvertToByte(source.Z), ConvertToByte(source.W)); + } + + /// + /// Converts premultiplied RGBA pixels to vectors. + /// + /// The source pixels. + /// The destination vectors. + /// The conversion modifier flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ToVector4( + ReadOnlySpan source, + Span destination, + PixelConversionModifiers modifiers) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + SimdUtils.ByteToNormalizedFloat(MemoryMarshal.Cast(source), MemoryMarshal.Cast(destination)); + ApplyForwardConversionModifiers(destination, modifiers.Remove(PixelConversionModifiers.Premultiply)); + } + + /// + /// Converts premultiplied RGBA pixels to unassociated scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + internal static void ToUnassociatedVector4(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + ref Rgba32P sourceBase = ref MemoryMarshal.GetReference(source); + ref Vector4 destinationBase = ref MemoryMarshal.GetReference(destination); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Rgba32P pixel = Unsafe.Add(ref sourceBase, i); + Unsafe.Add(ref destinationBase, i) = ToUnassociatedVector4(pixel.R, pixel.G, pixel.B, pixel.A); + } + } + + /// + /// Converts vectors to premultiplied RGBA pixels. + /// + /// The source vectors. + /// The destination pixels. + /// The conversion modifier flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void FromVector4( + Span source, + Span destination, + PixelConversionModifiers modifiers) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + ApplyBackwardConversionModifiers(source, modifiers.Remove(PixelConversionModifiers.Premultiply)); + SimdUtils.NormalizedFloatToByteSaturate(MemoryMarshal.Cast(source), MemoryMarshal.Cast(destination)); + } + + /// + /// Converts associated scaled vectors to premultiplied RGBA pixels. + /// + /// The source vectors. + /// The destination pixels. + internal static void FromAssociatedVector4(Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + ReassociateToByte(source); + SimdUtils.FloatToByteSaturate(MemoryMarshal.Cast(source), MemoryMarshal.Cast(destination)); + } + + /// + /// Converts premultiplied BGRA pixels to vectors. + /// + /// The source pixels. + /// The destination vectors. + /// The conversion modifier flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ToVector4( + ReadOnlySpan source, + Span destination, + PixelConversionModifiers modifiers) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + if (source.IsEmpty) + { + return; + } + + // ByteToNormalizedFloat preserves component order, so the packed BGRA bytes must first be shuffled to RGBA. + // Reuse the unwritten end of the larger Vector4 destination as staging to avoid a temporary allocation. + // The final vector overlaps that staging, so its source pixel is converted after the staged bytes are consumed. + int lastIndex = source.Length - 1; + Span temporary = MemoryMarshal.Cast(destination).Slice((3 * source.Length) + 1, lastIndex); + PixelConverter.FromBgra32.ToRgba32(MemoryMarshal.Cast(source[..lastIndex]), MemoryMarshal.Cast(temporary)); + SimdUtils.ByteToNormalizedFloat(MemoryMarshal.Cast(temporary), MemoryMarshal.Cast(destination[..lastIndex])); + destination[lastIndex] = source[lastIndex].ToVector4(); + ApplyForwardConversionModifiers(destination, modifiers.Remove(PixelConversionModifiers.Premultiply)); + } + + /// + /// Converts premultiplied BGRA pixels to unassociated scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + internal static void ToUnassociatedVector4(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + ref Bgra32P sourceBase = ref MemoryMarshal.GetReference(source); + ref Vector4 destinationBase = ref MemoryMarshal.GetReference(destination); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Bgra32P pixel = Unsafe.Add(ref sourceBase, i); + Unsafe.Add(ref destinationBase, i) = ToUnassociatedVector4(pixel.R, pixel.G, pixel.B, pixel.A); + } + } + + /// + /// Converts vectors to premultiplied BGRA pixels. + /// + /// The source vectors. + /// The destination pixels. + /// The conversion modifier flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void FromVector4( + Span source, + Span destination, + PixelConversionModifiers modifiers) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + ApplyBackwardConversionModifiers(source, modifiers.Remove(PixelConversionModifiers.Premultiply)); + + // NormalizedFloatToByteSaturate emits RGBA bytes, which can be shuffled in place to avoid a temporary pixel buffer. + Span destinationBytes = MemoryMarshal.Cast(destination); + SimdUtils.NormalizedFloatToByteSaturate(MemoryMarshal.Cast(source), destinationBytes); + PixelConverter.FromRgba32.ToBgra32(destinationBytes, destinationBytes); + } + + /// + /// Converts associated scaled vectors to premultiplied BGRA pixels. + /// + /// The source vectors. + /// The destination pixels. + internal static void FromAssociatedVector4(Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + ReassociateToByte(source); + + Span destinationBytes = MemoryMarshal.Cast(destination); + SimdUtils.FloatToByteSaturate(MemoryMarshal.Cast(source), destinationBytes); + PixelConverter.FromRgba32.ToBgra32(destinationBytes, destinationBytes); + } + + /// + /// Converts premultiplied ARGB pixels to vectors. + /// + /// The source pixels. + /// The destination vectors. + /// The conversion modifier flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ToVector4( + ReadOnlySpan source, + Span destination, + PixelConversionModifiers modifiers) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + if (source.IsEmpty) + { + return; + } + + // ByteToNormalizedFloat preserves component order, so the packed ARGB bytes must first be shuffled to RGBA. + // Reuse the unwritten end of the larger Vector4 destination as staging to avoid a temporary allocation. + // The final vector overlaps that staging, so its source pixel is converted after the staged bytes are consumed. + int lastIndex = source.Length - 1; + Span temporary = MemoryMarshal.Cast(destination).Slice((3 * source.Length) + 1, lastIndex); + PixelConverter.FromArgb32.ToRgba32(MemoryMarshal.Cast(source[..lastIndex]), MemoryMarshal.Cast(temporary)); + SimdUtils.ByteToNormalizedFloat(MemoryMarshal.Cast(temporary), MemoryMarshal.Cast(destination[..lastIndex])); + destination[lastIndex] = source[lastIndex].ToVector4(); + ApplyForwardConversionModifiers(destination, modifiers.Remove(PixelConversionModifiers.Premultiply)); + } + + /// + /// Converts premultiplied ARGB pixels to unassociated scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + internal static void ToUnassociatedVector4(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + ref Argb32P sourceBase = ref MemoryMarshal.GetReference(source); + ref Vector4 destinationBase = ref MemoryMarshal.GetReference(destination); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Argb32P pixel = Unsafe.Add(ref sourceBase, i); + Unsafe.Add(ref destinationBase, i) = ToUnassociatedVector4(pixel.R, pixel.G, pixel.B, pixel.A); + } + } + + /// + /// Converts vectors to premultiplied ARGB pixels. + /// + /// The source vectors. + /// The destination pixels. + /// The conversion modifier flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void FromVector4( + Span source, + Span destination, + PixelConversionModifiers modifiers) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + ApplyBackwardConversionModifiers(source, modifiers.Remove(PixelConversionModifiers.Premultiply)); + + // NormalizedFloatToByteSaturate emits RGBA bytes, which can be shuffled in place to avoid a temporary pixel buffer. + Span destinationBytes = MemoryMarshal.Cast(destination); + SimdUtils.NormalizedFloatToByteSaturate(MemoryMarshal.Cast(source), destinationBytes); + PixelConverter.FromRgba32.ToArgb32(destinationBytes, destinationBytes); + } + + /// + /// Converts associated scaled vectors to premultiplied ARGB pixels. + /// + /// The source vectors. + /// The destination pixels. + internal static void FromAssociatedVector4(Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + ReassociateToByte(source); + + Span destinationBytes = MemoryMarshal.Cast(destination); + SimdUtils.FloatToByteSaturate(MemoryMarshal.Cast(source), destinationBytes); + PixelConverter.FromRgba32.ToArgb32(destinationBytes, destinationBytes); + } + + /// + /// Converts premultiplied ABGR pixels to vectors. + /// + /// The source pixels. + /// The destination vectors. + /// The conversion modifier flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ToVector4( + ReadOnlySpan source, + Span destination, + PixelConversionModifiers modifiers) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + if (source.IsEmpty) + { + return; + } + + // ByteToNormalizedFloat preserves component order, so the packed ABGR bytes must first be shuffled to RGBA. + // Reuse the unwritten end of the larger Vector4 destination as staging to avoid a temporary allocation. + // The final vector overlaps that staging, so its source pixel is converted after the staged bytes are consumed. + int lastIndex = source.Length - 1; + Span temporary = MemoryMarshal.Cast(destination).Slice((3 * source.Length) + 1, lastIndex); + PixelConverter.FromAbgr32.ToRgba32(MemoryMarshal.Cast(source[..lastIndex]), MemoryMarshal.Cast(temporary)); + SimdUtils.ByteToNormalizedFloat(MemoryMarshal.Cast(temporary), MemoryMarshal.Cast(destination[..lastIndex])); + destination[lastIndex] = source[lastIndex].ToVector4(); + ApplyForwardConversionModifiers(destination, modifiers.Remove(PixelConversionModifiers.Premultiply)); + } + + /// + /// Converts premultiplied ABGR pixels to unassociated scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + internal static void ToUnassociatedVector4(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + ref Abgr32P sourceBase = ref MemoryMarshal.GetReference(source); + ref Vector4 destinationBase = ref MemoryMarshal.GetReference(destination); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Abgr32P pixel = Unsafe.Add(ref sourceBase, i); + Unsafe.Add(ref destinationBase, i) = ToUnassociatedVector4(pixel.R, pixel.G, pixel.B, pixel.A); + } + } + + /// + /// Converts vectors to premultiplied ABGR pixels. + /// + /// The source vectors. + /// The destination pixels. + /// The conversion modifier flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void FromVector4( + Span source, + Span destination, + PixelConversionModifiers modifiers) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + ApplyBackwardConversionModifiers(source, modifiers.Remove(PixelConversionModifiers.Premultiply)); + + // NormalizedFloatToByteSaturate emits RGBA bytes, which can be shuffled in place to avoid a temporary pixel buffer. + Span destinationBytes = MemoryMarshal.Cast(destination); + SimdUtils.NormalizedFloatToByteSaturate(MemoryMarshal.Cast(source), destinationBytes); + PixelConverter.FromRgba32.ToAbgr32(destinationBytes, destinationBytes); + } + + /// + /// Converts associated scaled vectors to premultiplied ABGR pixels. + /// + /// The source vectors. + /// The destination pixels. + internal static void FromAssociatedVector4(Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + ReassociateToByte(source); + + Span destinationBytes = MemoryMarshal.Cast(destination); + SimdUtils.FloatToByteSaturate(MemoryMarshal.Cast(source), destinationBytes); + PixelConverter.FromRgba32.ToAbgr32(destinationBytes, destinationBytes); + } + + /// + /// Converts a floating-point byte magnitude to a byte. + /// + /// The byte magnitude. + /// The rounded and clamped byte. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte ConvertToByte(float value) => (byte)Numerics.Clamp(value + .5F, 0, byte.MaxValue); + } +} diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index 9e649f3c08..33b91d1518 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -15,10 +15,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils; internal static partial class Vector4Converters { /// - /// Provides efficient implementations for batched to/from conversion. - /// which is applicable for -compatible pixel types where - /// returns the same scaled result as . - /// The method is works by internally converting to a therefore it's not applicable for that type! + /// Provides efficient implementations for batched conversion between RGBA-compatible pixel types and values. /// public static class RgbaCompatible { @@ -60,14 +57,14 @@ internal static partial class Vector4Converters return; } - // Using the last quarter of 'destination' as a temporary buffer to avoid allocation: + // ToVector4 expands each pixel into a 16-byte vector. Reuse the unwritten destination tail as RGBA staging + // so pixelOperations can reorder the source without allocating a temporary row. int countWithoutLastItem = count - 1; ReadOnlySpan reducedSource = source[..countWithoutLastItem]; Span lastQuarterOfDestination = MemoryMarshal.Cast(destination).Slice((3 * count) + 1, countWithoutLastItem); pixelOperations.ToRgba32(configuration, reducedSource, lastQuarterOfDestination); - // 'destination' and 'lastQuarterOfDestination' are overlapping buffers, - // but we are always reading/writing at different positions: + // Staging overlaps the final output vector, which remains unwritten until the staged bytes are consumed. SimdUtils.ByteToNormalizedFloat( MemoryMarshal.Cast(lastQuarterOfDestination), MemoryMarshal.Cast(destination[..countWithoutLastItem])); diff --git a/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs index 80b0963440..13ab9527fa 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs @@ -14,7 +14,6 @@ using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Benchmarks.Bulk; -[Config(typeof(Config.Short))] public abstract class FromVector4 where TPixel : unmanaged, IPixel { @@ -62,14 +61,21 @@ public abstract class FromVector4 => PixelOperations.Instance.FromVector4Destructive(this.Configuration, this.Source.GetSpan(), this.Destination.GetSpan()); } +[Config(typeof(Config.Short))] public class FromVector4Rgba32 : FromVector4 { + /// + /// Measures the raw SIMD kernel that converts normalized RGBA vector components to bytes. + /// [Benchmark] public void UseHwIntrinsics() { Span sBytes = MemoryMarshal.Cast(this.Source.GetSpan()); Span dFloats = MemoryMarshal.Cast(this.Destination.GetSpan()); + // Four components per pixel make every configured count divisible by the supported 128-, 256-, and 512-bit + // byte-vector widths. Calling the block kernel directly therefore excludes PixelOperations contract handling + // and the general conversion wrapper's remainder dispatch, establishing the lower bound for the SIMD conversion. SimdUtils.HwIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats); } @@ -121,36 +127,45 @@ public class FromVector4Rgba32 : FromVector4 } /* - BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3085/23H2/2023Update/SunValley3) - 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores - .NET SDK 8.0.200-preview.23624.5 - [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 - Job-YJYLLR : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 - - Runtime=.NET 8.0 Arguments=/p:DebugType=portable IterationCount=3 - LaunchCount=1 WarmupCount=3 - - | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | Allocated | Alloc Ratio | - |---------------------------- |------ |------------:|-------------:|-----------:|------:|--------:|----------:|------------:| - | PixelOperations_Base | 64 | 114.80 ns | 16.459 ns | 0.902 ns | 1.00 | 0.00 | - | NA | - | PixelOperations_Specialized | 64 | 28.91 ns | 80.482 ns | 4.411 ns | 0.25 | 0.04 | - | NA | - | FallbackIntrinsics128 | 64 | 133.60 ns | 23.750 ns | 1.302 ns | 1.16 | 0.02 | - | NA | - | ExtendedIntrinsic | 64 | 40.11 ns | 10.183 ns | 0.558 ns | 0.35 | 0.01 | - | NA | - | UseHwIntrinsics | 64 | 14.71 ns | 4.860 ns | 0.266 ns | 0.13 | 0.00 | - | NA | - | UseAvx2_Grouped | 64 | 20.23 ns | 11.619 ns | 0.637 ns | 0.18 | 0.00 | - | NA | - | | | | | | | | | | - | PixelOperations_Base | 256 | 387.94 ns | 31.591 ns | 1.732 ns | 1.00 | 0.00 | - | NA | - | PixelOperations_Specialized | 256 | 50.93 ns | 22.388 ns | 1.227 ns | 0.13 | 0.00 | - | NA | - | FallbackIntrinsics128 | 256 | 509.72 ns | 249.926 ns | 13.699 ns | 1.31 | 0.04 | - | NA | - | ExtendedIntrinsic | 256 | 140.32 ns | 9.353 ns | 0.513 ns | 0.36 | 0.00 | - | NA | - | UseHwIntrinsics | 256 | 41.99 ns | 16.000 ns | 0.877 ns | 0.11 | 0.00 | - | NA | - | UseAvx2_Grouped | 256 | 63.81 ns | 2.360 ns | 0.129 ns | 0.16 | 0.00 | - | NA | - | | | | | | | | | | - | PixelOperations_Base | 2048 | 2,979.49 ns | 2,023.706 ns | 110.926 ns | 1.00 | 0.00 | - | NA | - | PixelOperations_Specialized | 2048 | 326.19 ns | 19.077 ns | 1.046 ns | 0.11 | 0.00 | - | NA | - | FallbackIntrinsics128 | 2048 | 3,885.95 ns | 411.078 ns | 22.533 ns | 1.31 | 0.05 | - | NA | - | ExtendedIntrinsic | 2048 | 1,078.58 ns | 136.960 ns | 7.507 ns | 0.36 | 0.01 | - | NA | - | UseHwIntrinsics | 2048 | 312.07 ns | 68.662 ns | 3.764 ns | 0.10 | 0.00 | - | NA | - | UseAvx2_Grouped | 2048 | 451.83 ns | 41.742 ns | 2.288 ns | 0.15 | 0.01 | - | NA | + BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8737/25H2/2025Update/HudsonValley2) + AMD RYZEN AI MAX+ 395 w/ Radeon 8060S 3.00GHz, 1 CPU, 32 logical and 16 physical cores + .NET 8.0.28, X64 RyuJIT x86-64-v4 + + | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | Allocated | + |---------------------------- |------ |------------:|-----------:|----------:|------:|--------:|----------:| + | PixelOperations_Base | 64 | 60.19 ns | 32.376 ns | 1.775 ns | 1.00 | 0.04 | - | + | PixelOperations_Specialized | 64 | 22.94 ns | 66.082 ns | 3.622 ns | 0.38 | 0.05 | - | + | UseHwIntrinsics | 64 | 10.64 ns | 9.535 ns | 0.523 ns | 0.18 | 0.01 | - | + | UseAvx2_Grouped | 64 | 11.69 ns | 0.997 ns | 0.055 ns | 0.19 | 0.01 | - | + | PixelOperations_Base | 256 | 232.65 ns | 37.948 ns | 2.080 ns | 1.00 | 0.01 | - | + | PixelOperations_Specialized | 256 | 33.75 ns | 3.906 ns | 0.214 ns | 0.15 | 0.00 | - | + | UseHwIntrinsics | 256 | 21.91 ns | 2.732 ns | 0.150 ns | 0.09 | 0.00 | - | + | UseAvx2_Grouped | 256 | 24.84 ns | 5.333 ns | 0.292 ns | 0.11 | 0.00 | - | + | PixelOperations_Base | 2048 | 1,500.98 ns | 951.510 ns | 52.155 ns | 1.00 | 0.04 | - | + | PixelOperations_Specialized | 2048 | 156.21 ns | 65.074 ns | 3.567 ns | 0.10 | 0.00 | - | + | UseHwIntrinsics | 2048 | 144.38 ns | 40.947 ns | 2.244 ns | 0.10 | 0.00 | - | + | UseAvx2_Grouped | 2048 | 189.80 ns | 54.098 ns | 2.965 ns | 0.13 | 0.00 | - | + */ +} + +/// +/// Measures bulk conversion from values to premultiplied pixels. +/// +[Config(typeof(Config.Analysis))] +public class FromVector4Bgra32P : FromVector4 +{ + /* + BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8737/25H2/2025Update/HudsonValley2) + AMD RYZEN AI MAX+ 395 w/ Radeon 8060S 3.00GHz, 1 CPU, 32 logical and 16 physical cores + .NET 8.0.28, X64 RyuJIT x86-64-v4 + + | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | Code Size | Allocated | + |---------------------------- |------ |------------:|-----------:|----------:|------:|--------:|----------:|----------:| + | PixelOperations_Base | 64 | 59.73 ns | 19.644 ns | 1.077 ns | 1.00 | 0.02 | 1,152 B | - | + | PixelOperations_Specialized | 64 | 50.86 ns | 7.372 ns | 0.404 ns | 0.85 | 0.01 | 2,975 B | - | + | PixelOperations_Base | 256 | 214.03 ns | 70.798 ns | 3.881 ns | 1.00 | 0.02 | 1,152 B | - | + | PixelOperations_Specialized | 256 | 70.21 ns | 17.210 ns | 0.943 ns | 0.33 | 0.01 | 2,992 B | - | + | PixelOperations_Base | 2048 | 1,855.02 ns | 443.677 ns | 24.319 ns | 1.00 | 0.02 | 1,152 B | - | + | PixelOperations_Specialized | 2048 | 272.82 ns | 39.302 ns | 2.154 ns | 0.15 | 0.00 | 2,992 B | - | */ } diff --git a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs index 6499632b69..9777a2f3fc 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs @@ -20,24 +20,48 @@ public class ToVector4_Bgra32 : ToVector4 this.Destination.GetSpan()); } - // RESULTS: - // Method | Runtime | Count | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated | - // ---------------------------- |-------- |------ |-----------:|------------:|-----------:|-------:|---------:|-------:|----------:| - // PixelOperations_Base | Clr | 64 | 339.9 ns | 138.30 ns | 7.8144 ns | 1.00 | 0.00 | 0.0072 | 24 B | - // PixelOperations_Specialized | Clr | 64 | 338.1 ns | 13.30 ns | 0.7515 ns | 0.99 | 0.02 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Core | 64 | 245.6 ns | 29.05 ns | 1.6413 ns | 1.00 | 0.00 | 0.0072 | 24 B | - // PixelOperations_Specialized | Core | 64 | 257.1 ns | 37.89 ns | 2.1407 ns | 1.05 | 0.01 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Clr | 256 | 972.7 ns | 61.98 ns | 3.5020 ns | 1.00 | 0.00 | 0.0057 | 24 B | - // PixelOperations_Specialized | Clr | 256 | 882.9 ns | 126.21 ns | 7.1312 ns | 0.91 | 0.01 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Core | 256 | 910.0 ns | 90.87 ns | 5.1346 ns | 1.00 | 0.00 | 0.0067 | 24 B | - // PixelOperations_Specialized | Core | 256 | 448.4 ns | 15.77 ns | 0.8910 ns | 0.49 | 0.00 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Clr | 2048 | 6,951.8 ns | 1,299.01 ns | 73.3963 ns | 1.00 | 0.00 | - | 24 B | - // PixelOperations_Specialized | Clr | 2048 | 5,852.3 ns | 630.56 ns | 35.6279 ns | 0.84 | 0.01 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Core | 2048 | 6,937.5 ns | 1,692.19 ns | 95.6121 ns | 1.00 | 0.00 | - | 24 B | - // PixelOperations_Specialized | Core | 2048 | 2,994.5 ns | 1,126.65 ns | 63.6578 ns | 0.43 | 0.01 | - | 0 B | + // BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8737/25H2/2025Update/HudsonValley2) + // AMD RYZEN AI MAX+ 395 w/ Radeon 8060S 3.00GHz, 1 CPU, 32 logical and 16 physical cores + // .NET 8.0.28, X64 RyuJIT x86-64-v4 + // + // | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | Allocated | + // |---------------------------- |------ |------------:|------------:|----------:|------:|--------:|----------:| + // | PixelOperations_Base | 64 | 58.30 ns | 20.13 ns | 1.103 ns | 1.00 | 0.02 | - | + // | PixelOperations_Specialized | 64 | 58.95 ns | 23.68 ns | 1.298 ns | 1.01 | 0.03 | - | + // | PixelOperations_Base | 256 | 226.95 ns | 84.03 ns | 4.606 ns | 1.00 | 0.02 | - | + // | PixelOperations_Specialized | 256 | 229.70 ns | 40.00 ns | 2.192 ns | 1.01 | 0.02 | - | + // | PixelOperations_Base | 2048 | 1,795.42 ns | 1,465.95 ns | 80.354 ns | 1.00 | 0.05 | - | + // | PixelOperations_Specialized | 2048 | 291.89 ns | 109.98 ns | 6.028 ns | 0.16 | 0.01 | - | +} + +/// +/// Measures bulk conversion from premultiplied pixels to values. +/// +[Config(typeof(Config.Analysis))] +public class ToVector4_Bgra32P : ToVector4 +{ + /// + /// Measures the scalar implementation used as the comparison baseline. + /// + [Benchmark(Baseline = true)] + public void PixelOperations_Base() + { + new AssociatedAlphaPixelOperations().ToVector4( + this.Configuration, + this.Source.GetSpan(), + this.Destination.GetSpan()); + } + + // BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8737/25H2/2025Update/HudsonValley2) + // AMD RYZEN AI MAX+ 395 w/ Radeon 8060S 3.00GHz, 1 CPU, 32 logical and 16 physical cores + // .NET 8.0.28, X64 RyuJIT x86-64-v4 + // + // | Method | Count | Mean | Error | StdDev | Ratio | Code Size | Allocated | + // |---------------------------- |------ |------------:|-----------:|---------:|------:|----------:|----------:| + // | PixelOperations_Base | 64 | 58.08 ns | 10.122 ns | 0.555 ns | 1.00 | 932 B | - | + // | PixelOperations_Specialized | 64 | 79.63 ns | 9.080 ns | 0.498 ns | 1.37 | 3,688 B | - | + // | PixelOperations_Base | 256 | 224.99 ns | 46.742 ns | 2.562 ns | 1.00 | 958 B | - | + // | PixelOperations_Specialized | 256 | 99.41 ns | 9.728 ns | 0.533 ns | 0.44 | 3,688 B | - | + // | PixelOperations_Base | 2048 | 1,745.77 ns | 143.244 ns | 7.852 ns | 1.00 | 958 B | - | + // | PixelOperations_Specialized | 2048 | 293.00 ns | 44.137 ns | 2.419 ns | 0.17 | 3,704 B | - | } diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs index 9231fc8b4c..0c46de4707 100644 --- a/tests/ImageSharp.Benchmarks/Config.cs +++ b/tests/ImageSharp.Benchmarks/Config.cs @@ -8,6 +8,7 @@ using BenchmarkDotNet.Diagnostics.Windows; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Exporters.Json; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Reports; using BenchmarkDotNet.Toolchains.InProcess.Emit; @@ -46,6 +47,34 @@ public partial class Config : ManualConfig .WithArguments([new MsBuildArgument("/p:DebugType=portable")])); } + /// + /// Configures a short diagnostic run that exports memory usage, generated code, and full measurement data. + /// Elevated Windows runs also include branch-instruction counters. + /// + public class Analysis : Config + { + public Analysis() + { + this.AddJob(Job.ShortRun.WithRuntime(CoreRuntime.Core80).WithArguments([new MsBuildArgument("/p:DebugType=portable")])); + + this.AddDiagnoser(new DisassemblyDiagnoser(new DisassemblyDiagnoserConfig( + maxDepth: 3, + printSource: true, + exportGithubMarkdown: true, + exportCombinedDisassemblyReport: true))); + + this.AddExporter(JsonExporter.Full); + +#if OS_WINDOWS + if (this.IsElevated) + { + // ETW hardware counters require elevation, so ordinary benchmark runs omit them instead of requesting more access. + this.AddHardwareCounters(HardwareCounter.BranchMispredictions, HardwareCounter.BranchInstructions); + } +#endif + } + } + public class StandardInProcess : Config { public StandardInProcess() => this.AddJob( diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/AssociatedAlphaPixelBlenderBenchmark.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/AssociatedAlphaPixelBlenderBenchmark.cs new file mode 100644 index 0000000000..02f25f2ea3 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/AssociatedAlphaPixelBlenderBenchmark.cs @@ -0,0 +1,101 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.PixelBlenders; + +/// +/// Compares straight-alpha and associated-alpha bulk pixel blending. +/// +[Config(typeof(Config.Short))] +public class AssociatedAlphaPixelBlenderBenchmark +{ + private const int PixelCount = 1024; + + private readonly Rgba32[] unassociatedDestination = new Rgba32[PixelCount]; + private readonly Rgba32[] unassociatedBackground = new Rgba32[PixelCount]; + private readonly Rgba32[] unassociatedSource = new Rgba32[PixelCount]; + private readonly Rgba32P[] associatedDestination = new Rgba32P[PixelCount]; + private readonly Rgba32P[] associatedBackground = new Rgba32P[PixelCount]; + private readonly Rgba32P[] associatedSource = new Rgba32P[PixelCount]; + private readonly float[] amounts = new float[PixelCount]; + private readonly Vector4[] unassociatedWorkingBuffer = new Vector4[PixelCount * 3]; + private readonly Vector4[] associatedWorkingBuffer = new Vector4[PixelCount * 3]; + private PixelBlender unassociatedBlender; + private PixelBlender associatedBlender; + + /// + /// Gets or sets the color-blending mode measured by the benchmark. + /// + [ParamsAllValues] + public PixelColorBlendingMode ColorMode { get; set; } + + /// + /// Gets or sets the alpha-composition mode measured by the benchmark. + /// + [ParamsAllValues] + public PixelAlphaCompositionMode AlphaMode { get; set; } + + /// + /// Initializes equivalent straight-alpha and associated-alpha rows. + /// + [GlobalSetup] + public void Setup() + { + this.unassociatedBlender = PixelOperations.Instance.GetPixelBlender(this.ColorMode, this.AlphaMode); + this.associatedBlender = PixelOperations.Instance.GetPixelBlender(this.ColorMode, this.AlphaMode); + + Random random = new(42); + + for (int i = 0; i < PixelCount; i++) + { + Rgba32 background = new((byte)random.Next(256), (byte)random.Next(256), (byte)random.Next(256), (byte)random.Next(64, 256)); + Rgba32 source = new((byte)random.Next(256), (byte)random.Next(256), (byte)random.Next(256), (byte)random.Next(64, 256)); + + this.unassociatedBackground[i] = background; + this.unassociatedSource[i] = source; + this.associatedBackground[i] = Rgba32P.FromRgba32(background); + this.associatedSource[i] = Rgba32P.FromRgba32(source); + this.amounts[i] = random.NextSingle(); + } + } + + /// + /// Blends one row stored with straight alpha. + /// + /// The last destination pixel. + [Benchmark(Description = "Straight alpha", Baseline = true)] + public Rgba32 BlendUnassociated() + { + this.unassociatedBlender.Blend( + Configuration.Default, + this.unassociatedDestination, + this.unassociatedBackground, + this.unassociatedSource, + this.amounts, + this.unassociatedWorkingBuffer); + + return this.unassociatedDestination[^1]; + } + + /// + /// Blends one row stored with associated alpha. + /// + /// The last destination pixel. + [Benchmark(Description = "Associated alpha")] + public Rgba32P BlendAssociated() + { + this.associatedBlender.Blend( + Configuration.Default, + this.associatedDestination, + this.associatedBackground, + this.associatedSource, + this.amounts, + this.associatedWorkingBuffer); + + return this.associatedDestination[^1]; + } +} diff --git a/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs b/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs index 2eb821b61d..8897464539 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs @@ -35,6 +35,20 @@ public partial class ColorTests Assert.Equal(source, data); } + [Fact] + public void Rgba32P() + { + Rgba32P source = new(64, 32, 16, 128); + + // Act: + Color color = Color.FromPixel(source); + + // Assert: + Assert.Equal(PixelAlphaRepresentation.Associated, color.AlphaRepresentation); + Assert.Equal(source.ToScaledVector4(), color.ToScaledVector4()); + Assert.Equal(source, color.ToPixel()); + } + [Fact] public void Argb32() { diff --git a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs index 2e2f0d07db..66537007fb 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs @@ -114,6 +114,65 @@ public partial class ColorTests Assert.Equal(new L8(255), color.ToPixel()); } + [Fact] + public void AssociatedVectorConstructor() + { + Rgba32P expected = new(64, 32, 16, 128); + + // Act: + Color color = Color.FromScaledVector(expected.ToScaledVector4(), PixelAlphaRepresentation.Associated); + + // Assert: + Assert.Equal(PixelAlphaRepresentation.Associated, color.AlphaRepresentation); + Assert.Equal(expected.ToScaledVector4(), color.ToScaledVector4()); + Assert.Equal(expected, color.ToPixel()); + Assert.Equal(new Rgba32(128, 64, 32, 128), color.ToPixel()); + } + + [Fact] + public void UnassociatedPixelToAssociatedPixel() + { + Color color = Color.FromPixel(new Rgba32(128, 64, 32, 128)); + + // Act: + Rgba32P actual = color.ToPixel(); + + // Assert: + Assert.Equal(new Rgba32P(64, 32, 16, 128), actual); + } + + [Fact] + public void AssociatedVectorSpanConstructor() + { + Rgba32P expected = new(64, 32, 16, 128); + Vector4[] source = [expected.ToScaledVector4()]; + Color[] destination = new Color[source.Length]; + + // Act: + Color.FromScaledVector(source, destination, PixelAlphaRepresentation.Associated); + + // Assert: + Assert.Equal(PixelAlphaRepresentation.Associated, destination[0].AlphaRepresentation); + Assert.Equal(expected.ToScaledVector4(), destination[0].ToScaledVector4()); + Assert.Equal(expected, destination[0].ToPixel()); + } + + [Fact] + public void AssociatedPixelSpanRoundTrip() + { + Rgba32P[] source = [new(64, 32, 16, 128), new(24, 12, 6, 96)]; + Color[] colors = new Color[source.Length]; + Rgba32P[] destination = new Rgba32P[source.Length]; + + // Act: + Color.FromPixel(source, colors); + Color.ToPixel(colors, destination); + + // Assert: + Assert.Equal(source, destination); + Assert.All(colors, color => Assert.Equal(PixelAlphaRepresentation.Associated, color.AlphaRepresentation)); + } + [Fact] public void GenericPixelRoundTrip() { diff --git a/tests/ImageSharp.Tests/Color/ColorTests.cs b/tests/ImageSharp.Tests/Color/ColorTests.cs index c482fc9986..2594caeb5a 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.cs @@ -18,6 +18,28 @@ public partial class ColorTests Assert.Equal(expected, c2.ToPixel()); } + [Fact] + public void WithAlphaPreservesAssociatedRepresentation() + { + Color color = Color.FromPixel(new Rgba32P(64, 32, 16, 128)); + + // Act: + Color actual = color.WithAlpha(64F / 255F); + + // Assert: + Assert.Equal(PixelAlphaRepresentation.Associated, actual.AlphaRepresentation); + Assert.Equal(new Rgba32P(32, 16, 8, 64), actual.ToPixel()); + Assert.Equal(new Rgba32(128, 64, 32, 64), actual.ToPixel()); + } + + [Fact] + public void ToHexConvertsAssociatedRepresentation() + { + Color color = Color.FromPixel(new Rgba32P(64, 32, 16, 128)); + + Assert.Equal("80402080", color.ToHex()); + } + [Theory] [InlineData(false)] [InlineData(true)] diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index dfd0a6b307..aec8b6c43a 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -148,11 +148,11 @@ public partial class SimdUtilsTests { byte[] source = new Random(count).GenerateRandomByteArray(count); float[] result = new float[count]; - float[] expected = source.Select(b => b / 255f).ToArray(); + float[] expected = source.Select(b => b * (1F / byte.MaxValue)).ToArray(); convert(source, result); - Assert.Equal(expected, result, new ApproximateFloatComparer(1e-5f)); + Assert.Equal(expected, result); } [Theory] @@ -193,6 +193,45 @@ public partial class SimdUtilsTests } } + [Fact] + public void BulkConvertNormalizedFloatToByteRoundsMidpointsAwayFromZero() + { + float[] midpointValues = Enumerable.Range(0, byte.MaxValue).Select(x => (x + 0.5F) / byte.MaxValue).ToArray(); + float[] source = new float[1024]; + byte[] expected = new byte[source.Length]; + byte[] actual = new byte[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + int value = i % midpointValues.Length; + source[i] = midpointValues[value]; + expected[i] = (byte)(value + 1); + } + + SimdUtils.NormalizedFloatToByteSaturate(source, actual); + + Assert.Equal(expected, actual); + } + + [Fact] + public void BulkConvertFloatToByteRoundsMidpointsAwayFromZeroAndClampsOverflows() + { + float[] source = new float[1027]; + byte[] expected = new byte[source.Length]; + byte[] actual = new byte[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + float value = (i % 258) - .5F; + source[i] = value; + expected[i] = (byte)Math.Min(byte.MaxValue, Math.Max(0, value + .5F)); + } + + SimdUtils.FloatToByteSaturate(source, actual); + + Assert.Equal(expected, actual); + } + [Theory] [MemberData(nameof(ArbitraryArraySizes))] public void PackFromRgbPlanes_Rgb24(int count) => TestPackFromRgbPlanes( diff --git a/tests/ImageSharp.Tests/PixelFormats/AssociatedAlphaPixelTests.cs b/tests/ImageSharp.Tests/PixelFormats/AssociatedAlphaPixelTests.cs new file mode 100644 index 0000000000..dc4c108fcb --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/AssociatedAlphaPixelTests.cs @@ -0,0 +1,667 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +namespace SixLabors.ImageSharp.Tests.PixelFormats; + +/// +/// Verifies the shared contract for pixel formats that store associated alpha. +/// +/// The associated-alpha pixel format. +[Trait("Category", "PixelFormats")] +public abstract class AssociatedAlphaPixelTests + where TPixel : unmanaged, IPixel +{ + private static readonly ApproximateFloatComparer VectorComparer = new(.005F); + + /// + /// Gets the color channels described by the pixel format. + /// + protected virtual PixelColorType ExpectedColorType => PixelColorType.RGB | PixelColorType.Alpha; + + [Fact] + public void PixelInformationDescribesAssociatedAlpha() + { + PixelTypeInfo info = TPixel.GetPixelTypeInfo(); + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + int expectedComponentPrecision = (Unsafe.SizeOf() * 8) / 4; + + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(PixelAlphaRepresentation.Associated, info.AlphaRepresentation); + Assert.Equal(this.ExpectedColorType, info.ColorType); + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + + for (int i = 0; i < componentInfo.ComponentCount; i++) + { + Assert.Equal(expectedComponentPrecision, componentInfo.GetComponentPrecision(i)); + } + } + + [Fact] + public void ScaledVectorConversionsUseAssociatedComponents() + { + Vector4 associated = new(.25F, .125F, .0625F, .5F); + + TPixel pixel = TPixel.FromScaledVector4(associated); + + Assert.Equal(associated, pixel.ToScaledVector4(), VectorComparer); + } + + [Fact] + public void FromRgba32AssociatesColorComponents() + { + Rgba32 source = new(192, 128, 64, 128); + Vector4 expected = source.ToScaledVector4(); + Numerics.Premultiply(ref expected); + + TPixel pixel = TPixel.FromRgba32(source); + + Assert.Equal(expected, pixel.ToScaledVector4(), VectorComparer); + } + + [Fact] + public void ToRgba32ReturnsUnassociatedColorComponents() + { + Rgba32 expected = new(192, 128, 64, 128); + TPixel pixel = TPixel.FromRgba32(expected); + + Rgba32 actual = pixel.ToRgba32(); + + AssertRgba32Equal(expected, actual, 3); + } + + [Fact] + public void ColorConversionsPreserveUnassociatedColor() + { + Rgba32 expected = new(192, 128, 64, 128); + TPixel source = TPixel.FromRgba32(expected); + + Color color = Color.FromPixel(source); + Rgba32 actual = color.ToPixel(); + TPixel roundTrip = color.ToPixel(); + + AssertRgba32Equal(expected, actual, 3); + Assert.Equal(source.ToScaledVector4(), roundTrip.ToScaledVector4(), VectorComparer); + } + + [Fact] + public void ScalarBlendingUsesUnassociatedColorValues() + { + TPixel background = TPixel.FromRgba32(new Rgba32(200, 40, 80, 160)); + TPixel source = TPixel.FromRgba32(new Rgba32(20, 180, 100, 96)); + PixelBlender associatedBlender = PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.SrcOver); + PixelBlender unassociatedBlender = new DefaultPixelBlenders.NormalSrcOver(); + + Rgba32 expected = unassociatedBlender.Blend(background.ToRgba32(), source.ToRgba32(), .75F); + Rgba32 actual = associatedBlender.Blend(background, source, .75F).ToRgba32(); + + AssertRgba32Equal(expected, actual, 4); + } + + private static void AssertRgba32Equal(Rgba32 expected, Rgba32 actual, int tolerance) + { + Assert.InRange(Math.Abs(expected.R - actual.R), 0, tolerance); + Assert.InRange(Math.Abs(expected.G - actual.G), 0, tolerance); + Assert.InRange(Math.Abs(expected.B - actual.B), 0, tolerance); + Assert.InRange(Math.Abs(expected.A - actual.A), 0, tolerance); + } +} + +/// +/// Tests the pixel format. +/// +public class Rgba32PTests : AssociatedAlphaPixelTests +{ + [Fact] + public void ByteLayoutAndPackedValue() + { + Rgba32P[] pixels = [new(1, 2, 3, 4)]; + + Assert.Equal(new byte[] { 1, 2, 3, 4 }, MemoryMarshal.AsBytes(pixels.AsSpan()).ToArray()); + Assert.Equal(0x04030201U, pixels[0].PackedValue); + } +} + +/// +/// Tests the pixel format. +/// +public class Bgra32PTests : AssociatedAlphaPixelTests +{ + /// + protected override PixelColorType ExpectedColorType => PixelColorType.BGR | PixelColorType.Alpha; + + [Fact] + public void ByteLayoutAndPackedValue() + { + Bgra32P[] pixels = [new(1, 2, 3, 4)]; + + Assert.Equal(new byte[] { 3, 2, 1, 4 }, MemoryMarshal.AsBytes(pixels.AsSpan()).ToArray()); + Assert.Equal(0x04010203U, pixels[0].PackedValue); + } +} + +/// +/// Tests the pixel format. +/// +public class Argb32PTests : AssociatedAlphaPixelTests +{ + [Fact] + public void ByteLayoutAndPackedValue() + { + Argb32P[] pixels = [new(1, 2, 3, 4)]; + + Assert.Equal(new byte[] { 4, 1, 2, 3 }, MemoryMarshal.AsBytes(pixels.AsSpan()).ToArray()); + Assert.Equal(0x03020104U, pixels[0].PackedValue); + } +} + +/// +/// Tests the pixel format. +/// +public class Abgr32PTests : AssociatedAlphaPixelTests +{ + /// + protected override PixelColorType ExpectedColorType => PixelColorType.BGR | PixelColorType.Alpha; + + [Fact] + public void ByteLayoutAndPackedValue() + { + Abgr32P[] pixels = [new(1, 2, 3, 4)]; + + Assert.Equal(new byte[] { 4, 3, 2, 1 }, MemoryMarshal.AsBytes(pixels.AsSpan()).ToArray()); + Assert.Equal(0x01020304U, pixels[0].PackedValue); + } +} + +/// +/// Tests the pixel format. +/// +public class NormalizedByte4PTests : AssociatedAlphaPixelTests +{ + [Fact] + public void PackedValueMatchesNormalizedByte4ForAssociatedVector() + { + Vector4 associated = new(.1F, -.3F, .5F, -.7F); + + Assert.Equal(new NormalizedByte4(associated).PackedValue, new NormalizedByte4P(associated).PackedValue); + } +} + +/// +/// Tests the pixel format. +/// +public class HalfVector4PTests : AssociatedAlphaPixelTests +{ + [Fact] + public void PackedValueMatchesHalfVector4ForAssociatedVector() + { + Vector4 associated = new(.1F, -.3F, .5F, -.7F); + + Assert.Equal(new HalfVector4(associated).PackedValue, new HalfVector4P(associated).PackedValue); + } +} + +/// +/// Tests conversion between associated-alpha packed byte layouts. +/// +public class AssociatedAlphaPackedPixelConversionTests +{ + [Fact] + public void Rgba32PToBgra32PRoundTripIsLossless() => AssertLosslessRoundTrip(); + + [Fact] + public void Rgba32PToArgb32PRoundTripIsLossless() => AssertLosslessRoundTrip(); + + [Fact] + public void Rgba32PToAbgr32PRoundTripIsLossless() => AssertLosslessRoundTrip(); + + [Fact] + public void Rgba32PScalarAndBulkAssociatedVectorsAreEqual() => AssertScalarAndBulkAssociatedVectorsAreEqual((r, g, b, a) => new Rgba32P(r, g, b, a)); + + [Fact] + public void Bgra32PScalarAndBulkAssociatedVectorsAreEqual() => AssertScalarAndBulkAssociatedVectorsAreEqual((r, g, b, a) => new Bgra32P(r, g, b, a)); + + [Fact] + public void Argb32PScalarAndBulkAssociatedVectorsAreEqual() => AssertScalarAndBulkAssociatedVectorsAreEqual((r, g, b, a) => new Argb32P(r, g, b, a)); + + [Fact] + public void Abgr32PScalarAndBulkAssociatedVectorsAreEqual() => AssertScalarAndBulkAssociatedVectorsAreEqual((r, g, b, a) => new Abgr32P(r, g, b, a)); + + [Fact] + public void Rgba32PScalarAndBulkFromAssociatedVectorsAreEqual() => AssertScalarAndBulkFromAssociatedVectorsAreEqual(); + + [Fact] + public void Bgra32PScalarAndBulkFromAssociatedVectorsAreEqual() => AssertScalarAndBulkFromAssociatedVectorsAreEqual(); + + [Fact] + public void Argb32PScalarAndBulkFromAssociatedVectorsAreEqual() => AssertScalarAndBulkFromAssociatedVectorsAreEqual(); + + [Fact] + public void Abgr32PScalarAndBulkFromAssociatedVectorsAreEqual() => AssertScalarAndBulkFromAssociatedVectorsAreEqual(); + + private static void AssertLosslessRoundTrip() + where TIntermediate : unmanaged, IPixel + { + Rgba32P[] expected = + [ + new(0, 0, 0, 0), + new(1, 2, 3, 4), + new(31, 63, 95, 127), + new(64, 128, 192, 255), + new(255, 255, 255, 255), + ]; + TIntermediate[] intermediate = new TIntermediate[expected.Length]; + Rgba32P[] actual = new Rgba32P[expected.Length]; + + PixelOperations.Instance.From(Configuration.Default, expected, intermediate); + PixelOperations.Instance.From(Configuration.Default, intermediate, actual); + + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i].ToScaledVector4(), intermediate[i].ToScaledVector4()); + } + + Assert.Equal(expected, actual); + } + + private static void AssertScalarAndBulkAssociatedVectorsAreEqual(Func createPixel) + where TPixel : unmanaged, IPixel + { + TPixel[] pixels = new TPixel[64]; + Vector4[] actual = new Vector4[pixels.Length]; + + for (int i = 0; i < pixels.Length; i++) + { + int component = i * 4; + pixels[i] = createPixel((byte)component, (byte)(component + 1), (byte)(component + 2), (byte)(component + 3)); + } + + PixelOperations.Instance.ToAssociatedScaledVector4(Configuration.Default, pixels, actual); + + for (int i = 0; i < pixels.Length; i++) + { + Assert.Equal(pixels[i].ToScaledVector4(), actual[i]); + } + } + + private static void AssertScalarAndBulkFromAssociatedVectorsAreEqual() + where TPixel : unmanaged, IPixel + { + const int count = 259; + Vector4[] vectors = new Vector4[count]; + TPixel[] expected = new TPixel[count]; + TPixel[] actual = new TPixel[count]; + AssociatedAlphaPixelOperations operations = (AssociatedAlphaPixelOperations)PixelOperations.Instance; + + for (int i = 0; i < vectors.Length; i++) + { + // Alternating exact byte alpha values with fractional values covers both reassociation branches. The odd length also exercises the SIMD remainder. + float alpha = (i & 1) == 0 ? (i % 256) / 255F : ((i % 255) + .375F) / 255F; + vectors[i] = new Vector4(((i * 37) % 256) / 255F, ((i * 73) % 256) / 255F, ((i * 109) % 256) / 255F, 1F) * alpha; + vectors[i].W = alpha; + expected[i] = operations.FromAssociatedScaledVector4(vectors[i]); + } + + operations.FromAssociatedScaledVector4(Configuration.Default, vectors, actual); + + Assert.Equal(expected, actual); + } +} + +/// +/// Tests conversion between associated and unassociated packed byte formats. +/// +public class AssociatedToUnassociatedPackedPixelConversionTests +{ + [Fact] + public void Rgba32PToRgba32ScalarRoundTripPreservesEveryValidAssociatedComponent() + { + for (int alpha = 0; alpha <= byte.MaxValue; alpha++) + { + // Associated components greater than alpha are invalid, so the exhaustive domain is triangular rather than 256 squared. + for (int associated = 0; associated <= alpha; associated++) + { + // This integer expression is the exact nearest 8-bit unassociated value for associated * 255 / alpha. + byte unassociated = alpha == 0 ? (byte)0 : (byte)(((associated * byte.MaxValue) + (alpha / 2)) / alpha); + Rgba32P source = new((byte)associated, 0, 0, (byte)alpha); + Rgba32 expectedUnassociated = new(unassociated, 0, 0, (byte)alpha); + + Rgba32 actualUnassociated = source.ToRgba32(); + Rgba32P actualRoundTrip = Rgba32P.FromRgba32(actualUnassociated); + + Assert.Equal(expectedUnassociated, actualUnassociated); + Assert.Equal(source, actualRoundTrip); + } + } + } + + [Fact] + public void ColorFromRgba32PPreservesEveryValidAssociatedComponent() + { + for (int alpha = 0; alpha <= byte.MaxValue; alpha++) + { + for (int associated = 0; associated <= alpha; associated++) + { + byte unassociated = alpha == 0 ? (byte)0 : (byte)(((associated * byte.MaxValue) + (alpha / 2)) / alpha); + Rgba32P source = new((byte)associated, 0, 0, (byte)alpha); + Color color = Color.FromPixel(source); + + Assert.Equal(new Rgba32(unassociated, 0, 0, (byte)alpha), color.ToPixel()); + Assert.Equal(source, color.ToPixel()); + } + } + } + + [Fact] + public void Rgba32PToBgra32RoundTripPreservesEveryValidAssociatedComponent() + => AssertUnsignedByteBulkRoundTrip((red, green, blue, alpha) => new Rgba32P(red, green, blue, alpha)); + + [Fact] + public void Bgra32PToBgra32RoundTripPreservesEveryValidAssociatedComponent() + => AssertUnsignedByteBulkRoundTrip((red, green, blue, alpha) => new Bgra32P(red, green, blue, alpha)); + + [Fact] + public void Argb32PToBgra32RoundTripPreservesEveryValidAssociatedComponent() + => AssertUnsignedByteBulkRoundTrip((red, green, blue, alpha) => new Argb32P(red, green, blue, alpha)); + + [Fact] + public void Abgr32PToBgra32RoundTripPreservesEveryValidAssociatedComponent() + => AssertUnsignedByteBulkRoundTrip((red, green, blue, alpha) => new Abgr32P(red, green, blue, alpha)); + + [Fact] + public void NormalizedByte4PToRgba32ScalarRoundTripPreservesEveryValidAssociatedComponent() + { + for (int alpha = 0; alpha < byte.MaxValue; alpha++) + { + for (int associated = 0; associated <= alpha; associated++) + { + byte unassociated = alpha == 0 ? (byte)0 : (byte)(((associated * byte.MaxValue) + (alpha / 2)) / alpha); + byte unassociatedAlpha = (byte)(((alpha * byte.MaxValue) + 127) / 254); + NormalizedByte4P source = CreateNormalizedByte4P(associated, 0, 0, alpha); + + Rgba32 actualUnassociated = source.ToRgba32(); + NormalizedByte4P actualRoundTrip = NormalizedByte4P.FromRgba32(actualUnassociated); + + Assert.Equal(new Rgba32(unassociated, 0, 0, unassociatedAlpha), actualUnassociated); + Assert.Equal(source, actualRoundTrip); + } + } + } + + [Fact] + public void ColorFromNormalizedByte4PPreservesEveryValidAssociatedComponent() + { + for (int alpha = 0; alpha < byte.MaxValue; alpha++) + { + for (int associated = 0; associated <= alpha; associated++) + { + byte unassociated = alpha == 0 ? (byte)0 : (byte)(((associated * byte.MaxValue) + (alpha / 2)) / alpha); + byte unassociatedAlpha = (byte)(((alpha * byte.MaxValue) + 127) / 254); + NormalizedByte4P source = CreateNormalizedByte4P(associated, 0, 0, alpha); + Color color = Color.FromPixel(source); + + Assert.Equal(new Rgba32(unassociated, 0, 0, unassociatedAlpha), color.ToPixel()); + Assert.Equal(source, color.ToPixel()); + } + } + } + + [Fact] + public void NormalizedByte4PToBgra32RoundTripPreservesEveryValidAssociatedComponent() + { + const int pairCount = 32640; + const int channelCount = 3; + NormalizedByte4P[] source = new NormalizedByte4P[pairCount * channelCount]; + Bgra32[] expectedUnassociated = new Bgra32[source.Length]; + Bgra32[] actualUnassociated = new Bgra32[source.Length]; + NormalizedByte4P[] actualRoundTrip = new NormalizedByte4P[source.Length]; + int index = 0; + + for (int alpha = 0; alpha < byte.MaxValue; alpha++) + { + for (int associated = 0; associated <= alpha; associated++) + { + byte unassociated = alpha == 0 ? (byte)0 : (byte)(((associated * byte.MaxValue) + (alpha / 2)) / alpha); + byte unassociatedAlpha = (byte)(((alpha * byte.MaxValue) + 127) / 254); + + source[index] = CreateNormalizedByte4P(associated, 0, 0, alpha); + expectedUnassociated[index++] = new Bgra32(unassociated, 0, 0, unassociatedAlpha); + source[index] = CreateNormalizedByte4P(0, associated, 0, alpha); + expectedUnassociated[index++] = new Bgra32(0, unassociated, 0, unassociatedAlpha); + source[index] = CreateNormalizedByte4P(0, 0, associated, alpha); + expectedUnassociated[index++] = new Bgra32(0, 0, unassociated, unassociatedAlpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualUnassociated); + PixelOperations.Instance.From(Configuration.Default, actualUnassociated, actualRoundTrip); + + Assert.Equal(expectedUnassociated, actualUnassociated); + Assert.Equal(source, actualRoundTrip); + } + + private static void AssertUnsignedByteBulkRoundTrip(Func createPixel) + where TPixel : unmanaged, IPixel + { + const int pairCount = 32896; + const int channelCount = 3; + TPixel[] source = new TPixel[pairCount * channelCount]; + Bgra32[] expectedUnassociated = new Bgra32[source.Length]; + Bgra32[] actualUnassociated = new Bgra32[source.Length]; + TPixel[] actualRoundTrip = new TPixel[source.Length]; + int index = 0; + + for (int alpha = 0; alpha <= byte.MaxValue; alpha++) + { + // Associated components greater than alpha are invalid, so the exhaustive domain is triangular rather than 256 squared. + for (int associated = 0; associated <= alpha; associated++) + { + // This integer expression is the exact nearest 8-bit unassociated value for associated * 255 / alpha. + byte unassociated = alpha == 0 ? (byte)0 : (byte)(((associated * byte.MaxValue) + (alpha / 2)) / alpha); + + source[index] = createPixel((byte)associated, 0, 0, (byte)alpha); + expectedUnassociated[index++] = new Bgra32(unassociated, 0, 0, (byte)alpha); + source[index] = createPixel(0, (byte)associated, 0, (byte)alpha); + expectedUnassociated[index++] = new Bgra32(0, unassociated, 0, (byte)alpha); + source[index] = createPixel(0, 0, (byte)associated, (byte)alpha); + expectedUnassociated[index++] = new Bgra32(0, 0, unassociated, (byte)alpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualUnassociated); + PixelOperations.Instance.From(Configuration.Default, actualUnassociated, actualRoundTrip); + + Assert.Equal(expectedUnassociated, actualUnassociated); + Assert.Equal(source, actualRoundTrip); + } + + private static NormalizedByte4P CreateNormalizedByte4P(int red, int green, int blue, int alpha) + { + uint packed = (byte)(red - 127) + | ((uint)(byte)(green - 127) << 8) + | ((uint)(byte)(blue - 127) << 16) + | ((uint)(byte)(alpha - 127) << 24); + + return new NormalizedByte4P { PackedValue = packed }; + } +} + +/// +/// Tests that associated destinations use their stored alpha value when associating color components. +/// +public class AssociatedDestinationAlphaQuantizationTests +{ + [Fact] + public void Rgba32PQuantizesDestinationAlphaBeforeAssociation() => AssertUnsignedByteDestinationQuantizesAlphaBeforeAssociation(); + + [Fact] + public void Bgra32PQuantizesDestinationAlphaBeforeAssociation() => AssertUnsignedByteDestinationQuantizesAlphaBeforeAssociation(); + + [Fact] + public void Argb32PQuantizesDestinationAlphaBeforeAssociation() => AssertUnsignedByteDestinationQuantizesAlphaBeforeAssociation(); + + [Fact] + public void Abgr32PQuantizesDestinationAlphaBeforeAssociation() => AssertUnsignedByteDestinationQuantizesAlphaBeforeAssociation(); + + [Fact] + public void NormalizedByte4PQuantizesDestinationAlphaBeforeAssociation() + { + ReadOnlySpan components = [64, 127, 191]; + Rgba64[] source = new Rgba64[(ushort.MaxValue + 1) * components.Length]; + NormalizedByte4P[] actualBulk = new NormalizedByte4P[source.Length]; + int index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + foreach (byte component in components) + { + source[index++] = new Rgba64((ushort)(component * 257), 0, 0, (ushort)alpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualBulk); + index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + int expectedAlpha = ((alpha * 254) + 32767) / ushort.MaxValue; + + foreach (byte component in components) + { + int expectedRed = ((component * expectedAlpha) + 127) / byte.MaxValue; + NormalizedByte4P actualScalar = NormalizedByte4P.FromRgba64(source[index]); + int scalarRed = (sbyte)actualScalar.PackedValue + 127; + int scalarAlpha = (sbyte)(actualScalar.PackedValue >> 24) + 127; + int bulkRed = (sbyte)actualBulk[index].PackedValue + 127; + int bulkAlpha = (sbyte)(actualBulk[index].PackedValue >> 24) + 127; + + Assert.Equal(expectedRed, scalarRed); + Assert.Equal(expectedAlpha, scalarAlpha); + Assert.Equal(expectedRed, bulkRed); + Assert.Equal(expectedAlpha, bulkAlpha); + index++; + } + } + } + + [Fact] + public void HalfVector4PQuantizesDestinationAlphaBeforeAssociation() + { + ReadOnlySpan components = [64, 127, 191]; + Rgba64[] source = new Rgba64[(ushort.MaxValue + 1) * components.Length]; + HalfVector4P[] actualBulk = new HalfVector4P[source.Length]; + int index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + foreach (byte component in components) + { + source[index++] = new Rgba64((ushort)(component * 257), 0, 0, (ushort)alpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualBulk); + index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + float nativeAlpha = ((alpha / (float)ushort.MaxValue) * 2F) - 1F; + ushort expectedAlpha = BitConverter.HalfToUInt16Bits((Half)nativeAlpha); + float storedAlpha = ((float)BitConverter.UInt16BitsToHalf(expectedAlpha) + 1F) / 2F; + + foreach (byte component in components) + { + float associatedRed = ((component / (float)byte.MaxValue) * storedAlpha * 2F) - 1F; + ushort expectedRed = BitConverter.HalfToUInt16Bits((Half)associatedRed); + HalfVector4P actualScalar = HalfVector4P.FromRgba64(source[index]); + + Assert.Equal(expectedRed, (ushort)actualScalar.PackedValue); + Assert.Equal(expectedAlpha, (ushort)(actualScalar.PackedValue >> 48)); + Assert.Equal(expectedRed, (ushort)actualBulk[index].PackedValue); + Assert.Equal(expectedAlpha, (ushort)(actualBulk[index].PackedValue >> 48)); + index++; + } + } + } + + [Fact] + public void ColorToRgba32PUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + [Fact] + public void ColorToBgra32PUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + [Fact] + public void ColorToArgb32PUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + [Fact] + public void ColorToAbgr32PUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + [Fact] + public void ColorToNormalizedByte4PUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + [Fact] + public void ColorToHalfVector4PUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + /// + /// Verifies the unsigned-byte destination grid through scalar and bulk conversion entry points. + /// + /// The associated unsigned-byte pixel format. + private static void AssertUnsignedByteDestinationQuantizesAlphaBeforeAssociation() + where TPixel : unmanaged, IPixel + { + ReadOnlySpan components = [64, 127, 191]; + Rgba64[] source = new Rgba64[(ushort.MaxValue + 1) * components.Length]; + TPixel[] actualBulk = new TPixel[source.Length]; + int index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + foreach (byte component in components) + { + source[index++] = new Rgba64((ushort)(component * 257), 0, 0, (ushort)alpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualBulk); + index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + int expectedAlpha = ((alpha * byte.MaxValue) + 32767) / ushort.MaxValue; + + foreach (byte component in components) + { + int expectedRed = ((component * expectedAlpha) + 127) / byte.MaxValue; + Vector4 expected = new Vector4(expectedRed, 0, 0, expectedAlpha) * (1F / byte.MaxValue); + + Assert.Equal(expected, TPixel.FromRgba64(source[index]).ToScaledVector4()); + Assert.Equal(expected, actualBulk[index].ToScaledVector4()); + index++; + } + } + } + + /// + /// Verifies that delegates association to the destination pixel operations. + /// + /// The associated destination pixel format. + private static void AssertColorUsesDestinationAlphaRepresentation() + where TPixel : unmanaged, IPixel + { + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + ushort component = (ushort)((byte)alpha * 257); + Rgba64 source = new(component, 0, 0, (ushort)alpha); + TPixel expected = TPixel.FromRgba64(source); + TPixel actual = Color.FromPixel(source).ToPixel(); + + Assert.Equal(expected, actual); + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs index 36ddad6c64..d61ab88a63 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs @@ -222,6 +222,22 @@ public class PixelBlenderTests ExerciseAllBlenderModeCombinations, HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + [Fact] + public void AssociatedAlphaBlendFunctionsAreCalledForAllModeCombinations() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + ExerciseAllAssociatedAlphaBlenderModeCombinations, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + [Fact] + public void AssociatedHardLightDestAtopRoundsExactMidpointAwayFromZero() + { + Rgba32P background = Rgba32P.FromRgba32(new Rgba32(220, 80, 40, 160)); + Rgba32P source = Rgba32P.FromRgba32(new Rgba32(20, 180, 120, 96)); + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.HardLight, PixelAlphaCompositionMode.DestAtop); + + Assert.Equal(new Rgba32P(30, 33, 16, 60), blender.Blend(background, source, .625F)); + } + [Fact] public void Blend_WithConstantSourceAndSingleAmount() { @@ -624,22 +640,167 @@ public class PixelBlenderTests } } + private static void ExerciseAllAssociatedAlphaBlenderModeCombinations() + { + Rgba32P[] background = + [ + Rgba32P.FromRgba32(new Rgba32(220, 80, 40, 160)), + Rgba32P.FromRgba32(new Rgba32(40, 200, 100, 192)), + Rgba32P.FromRgba32(new Rgba32(120, 60, 230, 224)), + Rgba32P.FromRgba32(new Rgba32(180, 160, 20, 128)), + Rgba32P.FromRgba32(new Rgba32(30, 140, 210, 96)), + ]; + + Rgba32P[] source = + [ + Rgba32P.FromRgba32(new Rgba32(20, 180, 120, 96)), + Rgba32P.FromRgba32(new Rgba32(210, 30, 150, 144)), + Rgba32P.FromRgba32(new Rgba32(80, 220, 40, 176)), + Rgba32P.FromRgba32(new Rgba32(240, 110, 60, 208)), + Rgba32P.FromRgba32(new Rgba32(100, 50, 200, 112)), + ]; + + foreach (PixelAlphaCompositionMode alphaMode in Enum.GetValues()) + { + foreach (PixelColorBlendingMode colorMode in Enum.GetValues()) + { + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(colorMode, alphaMode); + AssertAssociatedBlenderMatchesScalar(blender, background, source, colorMode, alphaMode); + } + } + } + + private static void AssertAssociatedBlenderMatchesScalar( + PixelBlender blender, + Rgba32P[] associatedBackground, + Rgba32P[] associatedSource, + PixelColorBlendingMode colorMode, + PixelAlphaCompositionMode alphaMode) + { + const float amount = .625F; + float[] amounts = [.125F, .375F, .625F, .875F, 1F]; + float[] coverage = [.2F, .4F, .6F, .8F, 1F]; + Rgba32P constantAssociatedSource = associatedSource[2]; + + Rgba32P[] expected = new Rgba32P[associatedBackground.Length]; + Rgba32P[] actual = new Rgba32P[associatedBackground.Length]; + Vector4[] actualSourceSpanBuffer = new Vector4[associatedBackground.Length * 3]; + Vector4[] actualConstantSourceBuffer = new Vector4[associatedBackground.Length * 2]; + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = blender.Blend(associatedBackground[i], associatedSource[i], amount); + } + + blender.Blend(Configuration.Default, actual, associatedBackground, associatedSource, amount, actualSourceSpanBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "SourceSpanSingleAmount"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = blender.Blend(associatedBackground[i], constantAssociatedSource, amount); + } + + blender.Blend(Configuration.Default, actual, associatedBackground, constantAssociatedSource, amount, actualConstantSourceBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "ConstantSourceSingleAmount"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = blender.Blend(associatedBackground[i], associatedSource[i], amounts[i]); + } + + blender.Blend(Configuration.Default, actual, associatedBackground, associatedSource, amounts, actualSourceSpanBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "SourceSpanAmountSpan"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = blender.Blend(associatedBackground[i], constantAssociatedSource, amounts[i]); + } + + blender.Blend(Configuration.Default, actual, associatedBackground, constantAssociatedSource, amounts, actualConstantSourceBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "ConstantSourceAmountSpan"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = BlendWithCoverageScalar(blender, associatedBackground[i], associatedSource[i], amount, coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, actual, associatedBackground, associatedSource, amount, coverage, actualSourceSpanBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "SourceSpanSingleAmountCoverage"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = BlendWithCoverageScalar(blender, associatedBackground[i], constantAssociatedSource, amount, coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, actual, associatedBackground, constantAssociatedSource, amount, coverage, actualConstantSourceBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "ConstantSourceSingleAmountCoverage"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = BlendWithCoverageScalar(blender, associatedBackground[i], associatedSource[i], amounts[i], coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, actual, associatedBackground, associatedSource, amounts, coverage, actualSourceSpanBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "SourceSpanAmountSpanCoverage"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = BlendWithCoverageScalar(blender, associatedBackground[i], constantAssociatedSource, amounts[i], coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, actual, associatedBackground, constantAssociatedSource, amounts, coverage, actualConstantSourceBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "ConstantSourceAmountSpanCoverage"); + } + + private static Rgba32P BlendWithCoverageScalar(PixelBlender blender, Rgba32P background, Rgba32P source, float amount, float coverage) + { + Span destination = stackalloc Rgba32P[1]; + Span backgroundSpan = stackalloc Rgba32P[1] { background }; + Span sourceSpan = stackalloc Rgba32P[1] { source }; + Span coverageSpan = stackalloc float[1] { coverage }; + Span buffer = stackalloc Vector4[3]; + + // A one-pixel span takes the scalar remainder path, providing an exact oracle for each SIMD coverage result. + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, sourceSpan, amount, coverageSpan, buffer); + return destination[0]; + } + + private static void AssertRgba32PEqual( + ReadOnlySpan expected, + ReadOnlySpan actual, + PixelColorBlendingMode colorMode, + PixelAlphaCompositionMode alphaMode, + string scenario) + { + for (int i = 0; i < expected.Length; i++) + { + Assert.True(expected[i] == actual[i], $"{colorMode}/{alphaMode}/{scenario}[{i}]: expected {expected[i]}, actual {actual[i]}"); + } + } + private static void ExerciseBlender(PixelBlender blender) { Rgba32 background = Color.MistyRose.ToPixel(); Rgba32 source = Color.MidnightBlue.ToPixel(); + + ExerciseBlender(blender, background, source); + } + + 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]; - Rgba32 expected = blender.Blend(background, source, 1F); + TPixel expected = blender.Blend(background, source, 1F); - Rgba32[] destination = new Rgba32[4]; - Rgba32[] backgroundSpan = [background, background, background, background]; - Rgba32[] sourceSpan = [source, source, source, source]; + TPixel[] destination = new TPixel[4]; + TPixel[] backgroundSpan = [background, background, background, background]; + TPixel[] sourceSpan = [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); + 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); @@ -651,7 +812,7 @@ public class PixelBlenderTests blender.Blend(Configuration.Default, destination, backgroundSpan, source, amount, constantSourceBuffer); Assert.All(destination, x => Assert.Equal(expected, x)); - blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, sourceSpan, 1F, coverage, sourceSpanBuffer); + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, sourceSpan, 1F, coverage, sourceSpanBuffer); Assert.All(destination, x => Assert.Equal(expected, x)); blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, source, 1F, coverage, constantSourceBuffer); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs index 2a5c5765ab..935b4acc25 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; @@ -73,13 +74,11 @@ public abstract partial class PixelConverterTests } internal static void To( - Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) where TSourcePixel : unmanaged, IPixel where TDestinationPixel : unmanaged, IPixel { - Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); int count = sourcePixels.Length; @@ -92,13 +91,14 @@ public abstract partial class PixelConverterTests return; } - // Normal conversion + // Scalar source and destination boundaries are the reference for the bulk operation: each format owns any representation conversion and destination quantization it requires. ref TDestinationPixel destRef = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < count; i++) { ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); ref TDestinationPixel dp = ref Unsafe.Add(ref destRef, i); - dp = TDestinationPixel.FromScaledVector4(sp.ToScaledVector4()); + Vector4 vector = PixelOperations.Instance.ToUnassociatedScaledVector4(sp); + dp = PixelOperations.Instance.FromUnassociatedScaledVector4(vector); } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs index b372829270..f287a69958 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. // @@ -7,7 +7,6 @@ using SixLabors.ImageSharp.PixelFormats; using Xunit; using Xunit.Abstractions; - namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations; public partial class PixelOperationsTests @@ -43,6 +42,21 @@ public partial class PixelOperationsTests } } + public partial class Argb32P_OperationsTests : PixelOperationsTests + { + public Argb32P_OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = Argb32P.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); + } + } + public partial class Abgr32_OperationsTests : PixelOperationsTests { public Abgr32_OperationsTests(ITestOutputHelper output) @@ -58,6 +72,21 @@ public partial class PixelOperationsTests } } + public partial class Abgr32P_OperationsTests : PixelOperationsTests + { + public Abgr32P_OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = Abgr32P.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); + } + } + public partial class Bgr24_OperationsTests : PixelOperationsTests { public Bgr24_OperationsTests(ITestOutputHelper output) @@ -103,6 +132,21 @@ public partial class PixelOperationsTests } } + public partial class Bgra32P_OperationsTests : PixelOperationsTests + { + public Bgra32P_OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = Bgra32P.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); + } + } + public partial class Bgra4444_OperationsTests : PixelOperationsTests { public Bgra4444_OperationsTests(ITestOutputHelper output) @@ -193,6 +237,21 @@ public partial class PixelOperationsTests } } + public partial class HalfVector4P_OperationsTests : PixelOperationsTests + { + public HalfVector4P_OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = HalfVector4P.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); + } + } + public partial class L16_OperationsTests : PixelOperationsTests { public L16_OperationsTests(ITestOutputHelper output) @@ -283,6 +342,21 @@ public partial class PixelOperationsTests } } + public partial class NormalizedByte4P_OperationsTests : PixelOperationsTests + { + public NormalizedByte4P_OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = NormalizedByte4P.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); + } + } + public partial class NormalizedShort2_OperationsTests : PixelOperationsTests { public NormalizedShort2_OperationsTests(ITestOutputHelper output) @@ -388,6 +462,21 @@ public partial class PixelOperationsTests } } + public partial class Rgba32P_OperationsTests : PixelOperationsTests + { + public Rgba32P_OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = Rgba32P.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); + } + } + public partial class Rgba64_OperationsTests : PixelOperationsTests { public Rgba64_OperationsTests(ITestOutputHelper output) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude index ccf90fe40f..43dec7937a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude @@ -33,28 +33,41 @@ using Xunit.Abstractions; "Short4" ]; - private static readonly string[] AssociatedAlphaPixelTypes = []; + private static readonly string[] AssociatedAlphaPixelTypes = + [ + "Argb32P", + "Abgr32P", + "Bgra32P", + "HalfVector4P", + "NormalizedByte4P", + "Rgba32P" + ]; private static readonly string[] CommonPixelTypes = [ "A8", "Argb32", + "Argb32P", "Abgr32", + "Abgr32P", "Bgr24", "Bgr565", "Bgra32", + "Bgra32P", "Bgra4444", "Bgra5551", "Byte4", "HalfSingle", "HalfVector2", "HalfVector4", + "HalfVector4P", "L16", "L8", "La16", "La32", "NormalizedByte2", "NormalizedByte4", + "NormalizedByte4P", "NormalizedShort2", "NormalizedShort4", "Rg32", @@ -62,6 +75,7 @@ using Xunit.Abstractions; "Rgb48", "Rgba1010102", "Rgba32", + "Rgba32P", "Rgba64", "RgbaVector", "Short2", diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index 32b62fc03d..196c984494 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -70,7 +70,7 @@ public abstract class PixelOperationsTests : MeasureFixture protected virtual PixelOperations Operations { get; } = PixelOperations.Instance; - protected bool HasUnassociatedAlpha => TPixel.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Unassociated; + protected bool HasAssociatedAlpha => TPixel.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Associated; internal static TPixel[] CreateExpectedPixelData(Vector4[] source, RefAction vectorModifier = null) { @@ -185,7 +185,7 @@ public abstract class PixelOperationsTests : MeasureFixture { void SourceAction(ref Vector4 v) { - if (this.HasUnassociatedAlpha) + if (!this.HasAssociatedAlpha) { Numerics.Premultiply(ref v); } @@ -193,7 +193,7 @@ public abstract class PixelOperationsTests : MeasureFixture void ExpectedAction(ref Vector4 v) { - if (this.HasUnassociatedAlpha) + if (!this.HasAssociatedAlpha) { Numerics.UnPremultiply(ref v); } @@ -207,11 +207,11 @@ public abstract class PixelOperationsTests : MeasureFixture expected, (s, d) => { - PixelConversionModifiers modifiers = this.HasUnassociatedAlpha - ? PixelConversionModifiers.Premultiply - : PixelConversionModifiers.None; - - this.Operations.FromVector4Destructive(this.Configuration, s, d.GetSpan(), modifiers); + this.Operations.FromVector4Destructive( + this.Configuration, + s, + d.GetSpan(), + PixelConversionModifiers.Premultiply); }); } @@ -221,7 +221,7 @@ public abstract class PixelOperationsTests : MeasureFixture { void SourceAction(ref Vector4 v) { - if (this.HasUnassociatedAlpha) + if (!this.HasAssociatedAlpha) { Numerics.Premultiply(ref v); } @@ -229,7 +229,7 @@ public abstract class PixelOperationsTests : MeasureFixture void ExpectedAction(ref Vector4 v) { - if (this.HasUnassociatedAlpha) + if (!this.HasAssociatedAlpha) { Numerics.UnPremultiply(ref v); } @@ -243,15 +243,11 @@ public abstract class PixelOperationsTests : MeasureFixture expected, (s, d) => { - PixelConversionModifiers modifiers = this.HasUnassociatedAlpha - ? PixelConversionModifiers.Premultiply - : PixelConversionModifiers.None; - this.Operations.FromVector4Destructive( - this.Configuration, - s, - d.GetSpan(), - modifiers | PixelConversionModifiers.Scale); + this.Configuration, + s, + d.GetSpan(), + PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale); }); } @@ -263,7 +259,7 @@ public abstract class PixelOperationsTests : MeasureFixture { v = SRgbCompanding.Expand(v); - if (this.HasUnassociatedAlpha) + if (!this.HasAssociatedAlpha) { Numerics.Premultiply(ref v); } @@ -271,7 +267,7 @@ public abstract class PixelOperationsTests : MeasureFixture void ExpectedAction(ref Vector4 v) { - if (this.HasUnassociatedAlpha) + if (!this.HasAssociatedAlpha) { Numerics.UnPremultiply(ref v); } @@ -287,15 +283,11 @@ public abstract class PixelOperationsTests : MeasureFixture expected, (s, d) => { - PixelConversionModifiers modifiers = this.HasUnassociatedAlpha - ? PixelConversionModifiers.Premultiply - : PixelConversionModifiers.None; - this.Operations.FromVector4Destructive( - this.Configuration, - s, - d.GetSpan(), - modifiers | PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Scale); + this.Configuration, + s, + d.GetSpan(), + PixelConversionModifiers.Premultiply | PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Scale); }, false); } @@ -317,22 +309,27 @@ public abstract class PixelOperationsTests : MeasureFixture { new TestPixel(), new TestPixel(), + new TestPixel(), new TestPixel(), + new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), + new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), + new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), + new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), @@ -340,6 +337,7 @@ public abstract class PixelOperationsTests : MeasureFixture new TestPixel(), new TestPixel(), new TestPixel(), + new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), @@ -355,7 +353,7 @@ public abstract class PixelOperationsTests : MeasureFixture TPixel[] source = CreatePixelTestData(count); TDestPixel[] expected = new TDestPixel[count]; - PixelConverterTests.ReferenceImplementations.To(this.Configuration, source, expected); + PixelConverterTests.ReferenceImplementations.To(source, expected); TestOperation(source, expected, (s, d) => this.Operations.To(this.Configuration, s, d.GetSpan()), false); } @@ -408,7 +406,13 @@ public abstract class PixelOperationsTests : MeasureFixture { } - void ExpectedAction(ref Vector4 v) => Numerics.Premultiply(ref v); + void ExpectedAction(ref Vector4 v) + { + if (!this.HasAssociatedAlpha) + { + Numerics.Premultiply(ref v); + } + } TPixel[] source = CreatePixelTestData(count, SourceAction); Vector4[] expected = CreateExpectedVector4Data(source, ExpectedAction); @@ -427,7 +431,13 @@ public abstract class PixelOperationsTests : MeasureFixture { } - void ExpectedAction(ref Vector4 v) => Numerics.Premultiply(ref v); + void ExpectedAction(ref Vector4 v) + { + if (!this.HasAssociatedAlpha) + { + Numerics.Premultiply(ref v); + } + } TPixel[] source = CreateScaledPixelTestData(count, SourceAction); Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => ExpectedAction(ref v)); @@ -453,7 +463,11 @@ public abstract class PixelOperationsTests : MeasureFixture void ExpectedAction(ref Vector4 v) { v = SRgbCompanding.Expand(v); - Numerics.Premultiply(ref v); + + if (!this.HasAssociatedAlpha) + { + Numerics.Premultiply(ref v); + } } TPixel[] source = CreateScaledPixelTestData(count, SourceAction); @@ -499,7 +513,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { int i4 = i * 4; - Argb32 argb = Argb32.FromScaledVector4(source[i].ToScaledVector4()); + Argb32 argb = Argb32.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); expected[i4] = argb.A; expected[i4 + 1] = argb.R; @@ -543,7 +557,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { int i3 = i * 3; - Bgr24 bgr = Bgr24.FromScaledVector4(source[i].ToScaledVector4()); + Bgr24 bgr = Bgr24.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); expected[i3] = bgr.B; expected[i3 + 1] = bgr.G; expected[i3 + 2] = bgr.R; @@ -585,7 +599,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { int i4 = i * 4; - Bgra32 bgra = Bgra32.FromScaledVector4(source[i].ToScaledVector4()); + Bgra32 bgra = Bgra32.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); expected[i4] = bgra.B; expected[i4 + 1] = bgra.G; expected[i4 + 2] = bgra.R; @@ -628,7 +642,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { int i4 = i * 4; - Abgr32 abgr = Abgr32.FromScaledVector4(source[i].ToScaledVector4()); + Abgr32 abgr = Abgr32.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); expected[i4] = abgr.A; expected[i4 + 1] = abgr.B; expected[i4 + 2] = abgr.G; @@ -674,7 +688,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { int offset = i * size; - Bgra5551 bgra = Bgra5551.FromScaledVector4(source[i].ToScaledVector4()); + Bgra5551 bgra = Bgra5551.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); OctetBytes bytes = Unsafe.As(ref bgra); expected[offset] = bytes[0]; expected[offset + 1] = bytes[1]; @@ -714,7 +728,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { - expected[i] = L8.FromScaledVector4(source[i].ToScaledVector4()); + expected[i] = L8.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); } TestOperation( @@ -751,7 +765,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { - expected[i] = L16.FromScaledVector4(source[i].ToScaledVector4()); + expected[i] = L16.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); } TestOperation( @@ -793,7 +807,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { int offset = i * size; - La16 la = La16.FromScaledVector4(source[i].ToScaledVector4()); + La16 la = La16.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); OctetBytes bytes = Unsafe.As(ref la); expected[offset] = bytes[0]; expected[offset + 1] = bytes[1]; @@ -838,7 +852,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { int offset = i * size; - La32 la = La32.FromScaledVector4(source[i].ToScaledVector4()); + La32 la = La32.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); OctetBytes bytes = Unsafe.As(ref la); expected[offset] = bytes[0]; expected[offset + 1] = bytes[1]; @@ -882,7 +896,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { int i3 = i * 3; - Rgb24 rgb = Rgb24.FromScaledVector4(source[i].ToScaledVector4()); + Rgb24 rgb = Rgb24.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); expected[i3] = rgb.R; expected[i3 + 1] = rgb.G; expected[i3 + 2] = rgb.B; @@ -924,7 +938,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { int i4 = i * 4; - Rgba32 rgba = Rgba32.FromScaledVector4(source[i].ToScaledVector4()); + Rgba32 rgba = Rgba32.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); expected[i4] = rgba.R; expected[i4 + 1] = rgba.G; expected[i4 + 2] = rgba.B; @@ -967,7 +981,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { int i6 = i * 6; - Rgb48 rgb = Rgb48.FromScaledVector4(source[i].ToScaledVector4()); + Rgb48 rgb = Rgb48.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); OctetBytes rgb48Bytes = Unsafe.As(ref rgb); expected[i6] = rgb48Bytes[0]; expected[i6 + 1] = rgb48Bytes[1]; @@ -1013,7 +1027,7 @@ public abstract class PixelOperationsTests : MeasureFixture for (int i = 0; i < count; i++) { int i8 = i * 8; - Rgba64 rgba = Rgba64.FromScaledVector4(source[i].ToScaledVector4()); + Rgba64 rgba = Rgba64.FromScaledVector4(ToUnassociatedScaledVector4(source[i])); OctetBytes rgba64Bytes = Unsafe.As(ref rgba); expected[i8] = rgba64Bytes[0]; expected[i8 + 1] = rgba64Bytes[1]; @@ -1072,6 +1086,10 @@ public abstract class PixelOperationsTests : MeasureFixture return expected; } + private static Vector4 ToUnassociatedScaledVector4(TPixel source) + // The scalar conversion boundary owns representation-specific rounding. Byte-export tests compare each bulk operation with that scalar result. + => PixelOperations.Instance.ToUnassociatedScaledVector4(source); + internal static void TestOperation( TSource[] source, TDest[] expected, @@ -1208,15 +1226,34 @@ public abstract class PixelOperationsTests : MeasureFixture Assert.Equal(expected[i], actual[i], comparer); } } - else if (!this.PreferExactComparison && typeof(IPixel).IsAssignableFrom(typeof(TDest)) && IsComplexPixel()) + else if (!this.PreferExactComparison && typeof(IPixel).IsAssignableFrom(typeof(TDest))) { Span expected = this.ExpectedDestBuffer.AsSpan(); Span actual = this.ActualDestBuffer.GetSpan(); - ApproximateFloatComparer comparer = new(TestEnvironment.Is64BitProcess ? 0.0001F : 0.001F); - for (int i = 0; i < count; i++) + if (IsComplexPixel()) + { + ApproximateFloatComparer comparer = new(TestEnvironment.Is64BitProcess ? 0.0001F : 0.001F); + + for (int i = 0; i < count; i++) + { + Assert.Equal(((IPixel)expected[i]).ToScaledVector4(), ((IPixel)actual[i]).ToScaledVector4(), comparer); + } + } + else { - Assert.Equal(((IPixel)expected[i]).ToScaledVector4(), ((IPixel)actual[i]).ToScaledVector4(), comparer); + // SIMD and scalar conversion can select adjacent packed values at a quantization boundary. + int tolerance = Unsafe.SizeOf() <= sizeof(ushort) ? 17 : 1; + + for (int i = 0; i < count; i++) + { + Rgba32 expectedPixel = ((IPixel)expected[i]).ToRgba32(); + Rgba32 actualPixel = ((IPixel)actual[i]).ToRgba32(); + Assert.InRange(Math.Abs(expectedPixel.R - actualPixel.R), 0, tolerance); + Assert.InRange(Math.Abs(expectedPixel.G - actualPixel.G), 0, tolerance); + Assert.InRange(Math.Abs(expectedPixel.B - actualPixel.B), 0, tolerance); + Assert.InRange(Math.Abs(expectedPixel.A - actualPixel.A), 0, tolerance); + } } } else diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_Decode_Resize_giphy_150_150.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_Decode_Resize_giphy_150_150.png index 87c1fb7286..2449c3efdb 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_Decode_Resize_giphy_150_150.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_Decode_Resize_giphy_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cfd3afda359646aa3d46e1cffbfa7060395fda4c36c419ed3a2d8865284d090d -size 4031 +oid sha256:43e15b35bd282ecbf5f3b0b08b0f6fdba2e9144b0c17875d27bb8366b213c999 +size 4020 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/PngDecoder_Decode_Resize_splash_150_150.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/PngDecoder_Decode_Resize_splash_150_150.png index c697cd4c29..37323155f6 100644 --- a/tests/Images/External/ReferenceOutput/PngDecoderTests/PngDecoder_Decode_Resize_splash_150_150.png +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/PngDecoder_Decode_Resize_splash_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c87a09b28b3f857e13a2bb420d16caa131a67c45b0fe31404756042f30de6d0 -size 28139 +oid sha256:a6a0e244af51f47f725dfcf1ef60705dd3223269db98685a1078b57eb2273c31 +size 23988 diff --git a/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_Decode_Resize_rgb_a_rle_UL_150_150.png b/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_Decode_Resize_rgb_a_rle_UL_150_150.png index f3b7b0e809..8f63e3277e 100644 --- a/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_Decode_Resize_rgb_a_rle_UL_150_150.png +++ b/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_Decode_Resize_rgb_a_rle_UL_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb67006d156cff0fa8d4d1b131ac0eaa98279ae6dcc477818b2fc65f2dfb77aa -size 23396 +oid sha256:d0ce3abf8024859d1375721c18049513106ade2f9529ec20864621e310ca0b1b +size 18786 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_JpegCompressedWithIssue2679_Issue2679.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_JpegCompressedWithIssue2679_Issue2679.png index d2f11dc175..aded240a1e 100644 --- a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_JpegCompressedWithIssue2679_Issue2679.png +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_JpegCompressedWithIssue2679_Issue2679.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96729898fee87fc4305913c111a5841af205564d032b916aeb04425a20c6b22c -size 46540 +oid sha256:a94178ba1f0bd2edecdfd14c93196d1e195912f98875d875e1d79c8632676d03 +size 46525 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_Decode_Resize_RgbaUnassociatedAlpha3bit_150_150.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_Decode_Resize_RgbaUnassociatedAlpha3bit_150_150.png index 412af0ac7a..2f546df5b5 100644 --- a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_Decode_Resize_RgbaUnassociatedAlpha3bit_150_150.png +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_Decode_Resize_RgbaUnassociatedAlpha3bit_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:53b9dabffaae6a9250bf16f201ef8c9e933327874e8be91785c4fb6cd7e787a8 -size 10767 +oid sha256:10b471070b26a6aedfc0fce762dd71090409494ce0728790278d8584636672a2 +size 9940 diff --git a/tests/Images/External/ReferenceOutput/WebpDecoderTests/WebpDecoder_Decode_Resize_bike_lossless_150_150.png b/tests/Images/External/ReferenceOutput/WebpDecoderTests/WebpDecoder_Decode_Resize_bike_lossless_150_150.png index 35b99df985..4592d77da1 100644 --- a/tests/Images/External/ReferenceOutput/WebpDecoderTests/WebpDecoder_Decode_Resize_bike_lossless_150_150.png +++ b/tests/Images/External/ReferenceOutput/WebpDecoderTests/WebpDecoder_Decode_Resize_bike_lossless_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d73ae8bfad75c8b169fb050653eb4f8351edf173d1cc121ff1e23e3f1e594a97 -size 36342 +oid sha256:c372b6317bc4806ffdbddee45ded7b3a8c9f8ee7b7a462d8ff770b60ce00e176 +size 28629